% \iffalse meta-comment
%
%% File: l3pdf.dtx
%
% Copyright(C) 2019-2023 The LaTeX Project
%
% 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
%
%    http://www.latex-project.org/lppl.txt
%
% This file is part of the "l3kernel 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/latex3/latex3
%
% for those people who are interested.
%
%<*driver>
\documentclass[full]{l3doc}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{^^A
%   The \pkg{l3pdf} package\\ Core PDF support^^A
% }
%
% \author{^^A
%  The \LaTeX{} Project\thanks
%    {^^A
%      E-mail:
%        \href{mailto:latex-team@latex-project.org}
%          {latex-team@latex-project.org}^^A
%    }^^A
% }
%
% \date{Released 2023-08-11}
%
% \maketitle
%
% \begin{documentation}
%
% \section{Objects}
%
% \begin{function}[added = 2022-08-23]{\pdf_object_new:n}
%   \begin{syntax}
%     \cs{pdf_object_new:n} \Arg{object}
%   \end{syntax}
%   Declares \meta{object} as a PDF object. The object may be referenced
%   from this point on, and written later using \cs{pdf_object_write:nnn}.
% \end{function}
%
% \begin{function}[added = 2022-08-23]
%   {\pdf_object_write:nnn, \pdf_object_write:nnx}
%   \begin{syntax}
%     \cs{pdf_object_write:nn} \Arg{object} \Arg{type} \Arg{content}
%   \end{syntax}
%   Writes the \meta{content} as content of the \meta{object}. Depending on the
%   \meta{type} declared for the object, the format required for the
%   \meta{data} will vary
%   \begin{itemize}
%     \item[\texttt{array}] A space-separated list of values
%     \item[\texttt{dict}] Key--value pairs in the form
%       \texttt{/\meta{key} \meta{value}}
%     \item[\texttt{fstream}] Two brace groups: \meta{file name} and
%       \meta{file content}
%     \item[\texttt{stream}] Two brace groups: \meta{attributes (dictionary)}
%       and \meta{stream contents}
%   \end{itemize}
% \end{function}
%
% \begin{function}[EXP, added = 2021-02-10]{\pdf_object_ref:n}
%   \begin{syntax}
%     \cs{pdf_object_ref:n} \Arg{object}
%   \end{syntax}
%   Inserts the appropriate information to reference the \meta{object}
%   in for example page resource allocation
% \end{function}
%
% \begin{function}[added = 2021-02-10]
%   {\pdf_object_unnamed_write:nn, \pdf_object_unnamed_write:nx}
%   \begin{syntax}
%     \cs{pdf_object_unnamed_write:nn} \Arg{type} \Arg{content}
%   \end{syntax}
%   Writes the \meta{content} as content of an anonymous object. Depending on the
%   \meta{type}, the format required for the \meta{data} will vary
%   \begin{itemize}
%     \item[\texttt{array}] A space-separated list of values
%     \item[\texttt{dict}] Key--value pairs in the form
%       \texttt{/\meta{key} \meta{value}}
%     \item[\texttt{fstream}] Two brace groups: \meta{attributes (dictionary)}
%       and \meta{file name}
%     \item[\texttt{stream}] Two brace groups: \meta{attributes (dictionary)}
%       and \meta{stream contents}
%   \end{itemize}
% \end{function}
%
% \begin{function}[EXP, added = 2021-02-10]{\pdf_object_ref_last:}
%   \begin{syntax}
%     \cs{pdf_object_ref_last:}
%   \end{syntax}
%   Inserts the appropriate information to reference the last \meta{object}
%   created. This is particularly useful for anonymous objects.
% \end{function}
%
% \begin{function}[EXP, added = 2021-02-10]{\pdf_pageobject_ref:n}
%   \begin{syntax}
%     \cs{pdf_pagobject_ref:n} \Arg{pageobject}
%   \end{syntax}
%   Inserts the appropriate information to reference the \meta{pageobject}.
% \end{function}
%
% \begin{function}[EXP, pTF, added = 2020-05-15]{\pdf_object_if_exist:n}
%   \begin{syntax}
%    \cs{pdf_object_if_exist_p:n} \Arg{object}
%    \cs{pdf_object_if_exist:nTF} \Arg{object}
%   \end{syntax}
%   Tests whether an object with name \Arg{object} has been defined.
% \end{function}
%
% \section{Version}
%
% \begin{function}[pTF, EXP, added = 2021-02-10]{\pdf_version_compare:Nn}
%   \begin{syntax}
%     \cs{pdf_version_compare_p:Nn} \meta{comparator} \Arg{version}
%     \cs{pdf_version_compare:NnTF} \meta{comparator} \Arg{version} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   Compares the version of the PDF being created with the \meta{version}
%   string specified, using the \meta{comparator}. Either the \meta{true code}
%   or \meta{false code} will be left in the output stream.
% \end{function}
%
% \begin{function}[added = 2021-02-10]
%   {\pdf_version_gset:n, \pdf_version_min_gset:n}
%   \begin{syntax}
%     \cs{pdf_version_gset:n} \Arg{version}
%   \end{syntax}
%   Sets the \meta{version} of the PDF being created. The |min| version will
%   not alter the output version unless it is currently lower than the
%   \meta{version} requested.
%
%   This function may only be used up to the point where the PDF file is
%   initialised. With dvips it sets \cs{pdf_version_major:} and \cs{pdf_version_minor:}
%   and allows to compare the values with \cs{pdf_version_compare:Nn}, but the
%   PDF version itself still has to be set with the command line option
%   |-dCompatibilityLevel| of |ps2pdf|.
% \end{function}
%
% \begin{function}[EXP, added = 2021-02-10]
%   {\pdf_version:, \pdf_version_major:, \pdf_version_minor:}
%   \begin{syntax}
%     \cs{pdf_version:}
%   \end{syntax}
%   Expands to the currently-active PDF version.
% \end{function}
%
% \section{Page (media) size}
%
% \begin{function}[added = 2023-01-14]{\pdf_pagesize_gset:nn}
%   \begin{syntax}
%     \cs{pdf_pagesize_gset:nn} \Arg{width} \Arg{height}
%   \end{syntax}
%   Sets the page size (mediabox) of the PDF being created to the
%   \meta{width} and \meta{height}, both of which are \meta{dimexpr}.
% \end{function}
%
% \section{Compression}
%
% \begin{function}[added = 2021-02-10]{\pdf_uncompress:}
%   \begin{syntax}
%     \cs{pdf_uncompress:}
%   \end{syntax}
%   Disables any compression of the PDF, where possible.
%
%   This function may only be used up to the point where the PDF file is
%   initialised.
% \end{function}
%
% \section{Destinations}
%
% Destinations are the places a link jumped too.
% Unlike the name may suggest they don't described
% an exact location in the PDF. Instead a destination contains a reference to
% a page along with an instruction how to display this page.
% The normally used \enquote{XYZ \textit{top left zoom}} for example instructs
% the viewer to show the page with the given \textit{zoom} and
% the top left corner at the \textit{top left} coordinates---which then gives
% the impression that there is an anchor at this position.
%
% If an instruction takes a coordinate, it is calculated by the following
% commands relative to the location the command is issued.
% So to get a specific coordinate one has to move the command to the right place.
%
% \begin{function}[added = 2021-01-03]
%   {\pdf_destination:nn}
%   \begin{syntax}
%     \cs{pdf_destination:nn} \Arg{name} \Arg{type or integer}
%   \end{syntax}
%   This creates a destination. \Arg{type or integer} can be one of |fit|, |fith|,
%   |fitv|, |fitb|, |fitbh|, |fitbv|, |fitr|, |xyz|
%   or an integer representing a  scale factor in percent.
%   |fitr| here gives only a lightweight version of |/FitR|:
%   The backend code defines |fitr| so that it will with pdf\LaTeX{} and
%   Lua\LaTeX{} use the coordinates of the surrounding box,
%   with \texttt{dvips} and \texttt{dvipdfmx} it falls back to |fit|.
%   For full control use \cs{pdf_destination:nnnn}.
%
%   The keywords match to the PDF names as described in the following tabular.
%
%   \medskip
%   \noindent\begin{tabular}{ll>{\raggedright\arraybackslash}p{6cm}}
%   \toprule
%   Keyword & PDF & Remarks \\ \midrule
%   |fit|  & |/Fit|
%      & Fits the page to the window\\
%   |fith| & |/FitH|  \textit{top}
%      & Fits the width of the page to the window \\
%   |fitv| & |/FitV|  \textit{left}
%      & Fits the height of the page to the window \\
%   |fitb| & |/FitB|
%      & Fits the page bounding box to the window \\
%   |fitbh|& |/FitBH| \textit{top}
%      & Fits the width of the page bounding box to the window. \\
%   |fitbv|& |/FitBV| \textit{left}
%      & Fits the height of the page bounding box to the window. \\
%   |fitr| & |/FitR| \textit{left bottom right top}
%      & Fits the rectangle specified by the four coordinates to the window
%        (see above for the restrictions)\\
%   |xyz|  & |/XYZ|  \textit{left top} null
%      & Sets a coordinate but doesn't change the zoom.\\
%   \Arg{integer} & |/XYZ|  \textit{left top zoom}
%      & Sets a coordinate and a zoom meaning \Arg{integer}\%.
%    \\\bottomrule
%   \end{tabular}
%
% \end{function}
%
% \begin{function}[added = 2021-01-17]
%   {\pdf_destination:nnnn}
%   \begin{syntax}
%     \cs{pdf_destination:nnnn} \Arg{name} \Arg{width} \Arg{height} \Arg{depth}
%   \end{syntax}
%   This creates a destination with |/FitR| type with the given dimensions relative
%   to the current location. The destination is in a box of size zero, but it doesn't
%   switch to horizontal mode.
% \end{function}
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3pdf} implementation}
%
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=pdf>
%    \end{macrocode}
%
% \begin{variable}{\s_@@_stop}
%   Internal scan marks.
%    \begin{macrocode}
\scan_new:N \s_@@_stop
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\g_@@_init_bool}
%   A flag so we have some chance of avoiding setting things we are not
%   allowed to. As we are potentially early in the format, we have to work
%   a bit harder than ideal.
%    \begin{macrocode}
\bool_new:N \g_@@_init_bool
\bool_lazy_and:nnT
  { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
  { \tl_if_exist_p:N \@expl@finalise@setup@@@@ }
  {
    \tl_gput_right:Nn \@expl@finalise@setup@@@@
      {
        \tl_gput_right:Nn \@kernel@after@begindocument
          { \bool_gset_true:N \g_@@_init_bool }
      }
  }
%    \end{macrocode}
% \end{variable}
%
% \subsection{Compression}
%
% \begin{macro}{\pdf_uncompress:}
%   Simple to do.
%    \begin{macrocode}
\cs_new_protected:Npn \pdf_uncompress:
  {
    \bool_if:NF \g_@@_init_bool
      {
        \@@_backend_compresslevel:n { 0 }
        \@@_backend_compress_objects:n { \c_false_bool }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Objects}
%
% \begin{macro}{\pdf_object_new:n}
% \begin{macro}{\pdf_object_write:nnn, \pdf_object_write:nnx}
% \begin{macro}{\pdf_object_ref:n}
% \begin{macro}{\pdf_object_unnamed_write:nn, \pdf_object_unnamed_write:nx}
% \begin{macro}{\pdf_object_ref_last:}
% \begin{macro}[pTF]{\pdf_object_if_exist:n}
%   Simple to do: all objects create a constant |int| so it is not a
%   backend-specific name.
%    \begin{macrocode}
\cs_new_protected:Npn \pdf_object_new:n #1
  {
    \@@_backend_object_new:n {#1}
    \cs_new_eq:cc
      { c_@@_backend_object_ \tl_to_str:n {#1} _int }
      { c_@@_object_ \tl_to_str:n {#1} _int }
  }
\cs_new_protected:Npn \pdf_object_write:nnn #1#2#3
  {
    \@@_backend_object_write:nnn {#1} {#2} {#3}
    \bool_gset_true:N \g_@@_init_bool
  }
\cs_generate_variant:Nn \pdf_object_write:nnn { nnx }
\cs_new:Npn \pdf_object_ref:n #1 { \@@_backend_object_ref:n {#1} }
\cs_new_protected:Npn \pdf_object_unnamed_write:nn #1#2
  {
    \@@_backend_object_now:nn {#1} {#2}
    \bool_gset_true:N \g_@@_init_bool
  }
\cs_generate_variant:Nn \pdf_object_unnamed_write:nn { nx }
\cs_new:Npn \pdf_object_ref_last: { \@@_backend_object_last: }
\prg_new_conditional:Npnn \pdf_object_if_exist:n #1 { p , T , F , TF }
  {
    \int_if_exist:cTF { c_@@_object_ \tl_to_str:n {#1} _int }
      \prg_return_true:
      \prg_return_false:
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\pdf_pageobject_ref:n}
%    \begin{macrocode}
\cs_new:Npn \pdf_pageobject_ref:n #1
  { \@@_backend_pageobject_ref:n {#1} }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Version}
%
% \begin{macro}{\pdf_version_compare:Nn}
% \begin{macro}
%   {
%     @@_version_compare_=:w ,
%     @@_version_compare_<:w ,
%     @@_version_compare_>:w
%   }
%   To compare version, we need to split the given value then deal with both
%   major and minor version
%    \begin{macrocode}
\prg_new_conditional:Npnn \pdf_version_compare:Nn #1#2 { p , T , F , TF }
  { \use:c { @@_version_compare_ #1 :w } #2 . . \s_@@_stop }
\cs_new:cpn { @@_version_compare_=:w } #1 . #2 . #3 \s_@@_stop
 {
   \bool_lazy_and:nnTF
    { \int_compare_p:nNn \@@_backend_version_major: = {#1} }
    { \int_compare_p:nNn \@@_backend_version_minor: = {#2} }
    { \prg_return_true: }
    { \prg_return_false: }
 }
\cs_new:cpn { @@_version_compare_<:w } #1 . #2 . #3 \s_@@_stop
 {
   \bool_lazy_or:nnTF
    { \int_compare_p:nNn \@@_backend_version_major: < {#1} }
    {
      \bool_lazy_and_p:nn
        { \int_compare_p:nNn \@@_backend_version_major: = {#1} }
        { \int_compare_p:nNn \@@_backend_version_minor: < {#2} }
    }
    { \prg_return_true: }
    { \prg_return_false: }
 }
\cs_new:cpn { @@_version_compare_>:w } #1 . #2 . #3 \s_@@_stop
 {
   \bool_lazy_or:nnTF
    { \int_compare_p:nNn \@@_backend_version_major: > {#1} }
    {
      \bool_lazy_and_p:nn
        { \int_compare_p:nNn \@@_backend_version_major: = {#1} }
        { \int_compare_p:nNn \@@_backend_version_minor: > {#2} }
    }
    { \prg_return_true: }
    { \prg_return_false: }
 }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\pdf_version_gset:n, \pdf_version_min_gset:n}
% \begin{macro}{\@@_version_gset:w}
%   Split the version and set.
%    \begin{macrocode}
\cs_new_protected:Npn \pdf_version_gset:n #1
  { \@@_version_gset:w  #1 . . \s_@@_stop }
\cs_new_protected:Npn \pdf_version_min_gset:n #1
  {
    \pdf_version_compare:NnT < {#1}
      { \@@_version_gset:w  #1 . . \s_@@_stop }
  }
\cs_new_protected:Npn \@@_version_gset:w  #1 . #2 . #3\s_@@_stop
  {
    \bool_if:NF \g_@@_init_bool
      {
        \@@_backend_version_major_gset:n {#1}
        \@@_backend_version_minor_gset:n {#2}
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\pdf_version:, \pdf_version_major:, \pdf_version_minor:}
%   Wrappers.
%    \begin{macrocode}
\cs_new:Npn \pdf_version:
  { \@@_backend_version_major: . \@@_backend_version_minor: }
\cs_new:Npn \pdf_version_major: { \@@_backend_version_major: }
\cs_new:Npn \pdf_version_minor: { \@@_backend_version_minor: }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Page size}
%
% \begin{macro}{\pdf_pagesize_gset:nn}
%    \begin{macrocode}
\cs_new_protected:Npn \pdf_pagesize_gset:nn #1#2
  { \@@_backend_pagesize_gset:nn {#1} {#2} }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Destinations}
%
% \begin{macro}{\pdf_destination:nn}
%    \begin{macrocode}
\cs_new_protected:Npn \pdf_destination:nn #1#2
  { \@@_backend_destination:nn {#1} {#2} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\pdf_destination:nnnn}
%    \begin{macrocode}
\cs_new_protected:Npn \pdf_destination:nnnn #1#2#3#4
  {
    \hbox_to_zero:n
      { \@@_backend_destination:nnnn {#1} {#2} {#3} {#4} }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{PDF Page size (media box)}
%
%  Everything here is delayed to the start of the document so that the
%  backend will definitely be loaded.
%    \begin{macrocode}
\cs_if_exist:NT \@kernel@before@begindocument
  {
    \tl_gput_right:Nn \@kernel@before@begindocument
      {
        \bool_lazy_all:nT
          {
            { \cs_if_exist_p:N \stockheight }
            { \cs_if_exist_p:N \stockwidth }
            { \cs_if_exist_p:N \IfDocumentMetadataTF }
            { \IfDocumentMetadataTF { \c_true_bool } { \c_false_bool } }
            { \int_compare_p:nNn \tex_mag:D = { 1000 } }
          }
          {
            \bool_lazy_and:nnTF
              { \dim_compare_p:nNn \stockheight > { 0pt } }
              { \dim_compare_p:nNn \stockwidth  > { 0pt } }
              {
                \@@_backend_pagesize_gset:nn
                  \stockwidth \stockheight
              }
              {
                \bool_lazy_or:nnF
                  { \dim_compare_p:nNn \stockheight < { 0pt } }
                  { \dim_compare_p:nNn \stockwidth  < { 0pt } }
                  {
                    \bool_lazy_and:nnT
                      { \dim_compare_p:nNn \paperheight > { 0pt } }
                      { \dim_compare_p:nNn \paperwidth  > { 0pt } }
                      {
                        \@@_backend_pagesize_gset:nn
                          \paperwidth \paperheight
                      }
                  }
              }
          }
      }
  }
%    \end{macrocode}
%
% \subsection{Deprecated functions}
%
% \begin{variable}{\g_@@_object_prop}
%   For tracking objects.
%    \begin{macrocode}
\prop_new:N \g_@@_object_prop
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\pdf_object_new:nn, \pdf_object_write:nn, \pdf_object_write:nx}
%   Wrap up the type data in a prop.
%    \begin{macrocode}
\cs_new_protected:Npn \pdf_object_new:nn #1#2
  {
    \prop_gput:Nnn \g_@@_object_prop {#1} {#2}
    \@@_backend_object_new:n {#1}
  }
\cs_new_protected:Npn \pdf_object_write:nn #1#2
  {
    \exp_args:Nnx \@@_backend_object_write:nnn
      {#1} { \prop_item:Nn \g_@@_object_prop {#1} } {#2}
    \bool_gset_true:N \g_@@_init_bool
  }
\cs_generate_variant:Nn \pdf_object_write:nn { nx }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex
