% \iffalse meta-comment % %% File: l3graphics.dtx % % Copyright (C) 2017-2024 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 "l3experimental 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} % % \fi % % \title{^^A % The \pkg{l3graphics} package\\ Graphics inclusion 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 2024-03-14} % % \maketitle % % \begin{documentation} % % \section{\pkg{l3graphics} documentation} % % \subsection{Graphics keys} % % Inclusion of graphic files requires a range of low-level data be passed to % the backend. This is set up using a small number of key--value settings, % which are stored in the |graphics| tree. % % \begin{variable}{decodearray} % Array to decode color in bitmap graphic: when non-empty, this should % be in the form of one, two or three pairs of real numbers in the range % $[0,1]$, separated by spaces. % \end{variable} % % \begin{variable}{draft} % Switch to enable draft mode: graphics are read but not included when this is % true. % \end{variable} % % \begin{variable}{interpolate} % Switch which indicates whether interpolation should be applied to bitmap % graphic files. % \end{variable} % % \begin{variable}{page} % The page to extract from a multi-page graphic file: used for |.pdf| files % which may contain multiple pages. % \end{variable} % % \begin{variable}{pdf-attr} % Additional PDF-focussed attributes: available to allow control of % extended |.pdf| structures beyond those needed for graphic inclusion. % Due to backend restrictions, this key is only functional with direct % PDF mode (pdf\TeX{} and Lua\TeX{}). % \end{variable} % % \begin{variable}{pagebox} % The nature of the page box setting used to determine the bounding box of % material: used for |.pdf| files which feature multiple page box % specifications. A choice from |art|, |bleed|, |crop|, |media|, |trim|. % The standard setting is |crop|. % \end{variable} % % \begin{variable}{type} % The type of graphic file beign included: if this key is not set, the % \emph{type} is determined from the file extension. % \end{variable} % % \subsection{Including graphics} % % \begin{function}{\graphics_include:nn, \graphics_include:nV} % \begin{syntax} % \cs{graphics_include:nn} \Arg{keys} \Arg{file} % \end{syntax} % Horizontal-mode command which includes the \meta{file} as an graphic % at the current location. The file \meta{type} may be given as one of the % \meta{keys}, or will otherwise be determined from file extension. The % \meta{keys} is used to pass settings as detailed above. % \end{function} % % \begin{variable}{\l_graphics_ext_type_prop} % Defines mapping between file extensions and file types; where there is % no entry for an extension, the type is assumed to be the extension % with the leading |.| removed. Entries should be made in lower case, and % the key should be an extension including the leading |.|, for example % \begin{verbatim} % \prop_put:Nnn \l_graphics_ext_type_prop { .ps } { eps } % \end{verbatim} % \end{variable} % % \begin{variable}{\l_graphics_search_path_seq} % Each entry is the path to a directory which should be searched when % seeking an graphic file. Each path can be relative or absolute, and should % not include the trailing slash. The entries are not expanded when % used so may contain active characters but should not feature any % variable content. Spaces need not be quoted. % \end{variable} % % \subsection{Utility functions} % % \begin{function}[noTF]{\graphics_get_full_name:nN} % \begin{syntax} % \cs{graphics_get_full_name:nN} \Arg{file} \meta{tl~var} % \cs{graphics_get_full_name:nNTF} \Arg{file} \meta{tl~var} \Arg{true code} \Arg{false code} % \end{syntax} % Searches for \meta{file} first as given and then using the extensions % listed in \cs{l_graphics_search_ext_seq}. The search path used will be % the entries of \cs{l_graphics_search_path_seq}. If found, the full file % name including any path and extension will be returned in the % \meta{tl~var}. In the non-branching version, the \meta{tl var} will be % set to \cs{q_no_value} in the case that the graphics is not found. % \end{function} % % \begin{variable}{\l_graphics_search_ext_seq} % Extensions to use for graphic searching when the given \meta{file} name is not % found by \cs{graphics_get_full_name:nN}. % \end{variable} % % \begin{function}{\graphics_get_pagecount:nN} % \begin{syntax} % \cs{graphics_get_pagecount:nn} \Arg{file} \meta{tl~var} % \end{syntax} % Reads the graphics \meta{file} and extracts the number of pages, which % are stored in the \meta{tl~var}. % \end{function} % % \subsection{Showing and logging included graphics} % % \begin{function}{\graphics_show_list:, \graphics_log_list:} % \begin{syntax} % \cs{graphics_show_list:} % \cs{graphics_log_list:} % \end{syntax} % These functions list all graphic files loaded by in a similar manner to % \cs{file_show_list:} and \cs{file_log_list:}. While % \cs{graphics_show_list:} displays the list in the terminal, % \cs{graphics_log_list:} outputs it to the log file only. In both cases, only % graphics loaded by \pkg{l3graphics} are listed. % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3graphics} implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % \begin{macrocode} %<@@=graphics> % \end{macrocode} % % \begin{macrocode} \ProvidesExplPackage{l3graphics}{2024-03-14}{} {L3 Experimental graphics inclusion support} % \end{macrocode} % % \begin{variable}{\l_@@_internal_dim, \l_@@_internal_ior, \l_@@_internal_tl} % Scratch space. % \begin{macrocode} \dim_new:N \l_@@_internal_dim \ior_new:N \l_@@_internal_ior \tl_new:N \l_@@_internal_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\s_@@_stop} % Internal scan marks. % \begin{macrocode} \scan_new:N \s_@@_stop % \end{macrocode} % \end{variable} % % \subsection{Graphics keys} % % \begin{macro} % { % \l_@@_decodearray_str , % \l__@@_draft_bool , % \l_@@_interpolate_bool , % \l_@@_page_int , % \l_@@_pagebox_tl , % \l_@@_pdf_str , % \l_@@_type_str % } % Keys which control features of graphics. The standard value of |pagebox| % set up here should match the default for the backends themselves: in % the absence of any other setting the |crop| should be used. Note that % the variable \cs{l_@@_pagebox_str} can be empty internally, as backends % which do not support |pagebox| are set up to clear it entirely. The % store for |pagebox| is a |tl| as that makes extracting the data % easier for some backends. % \begin{macrocode} \tl_new:N \l_@@_pagebox_tl \keys_define:nn { graphics } { decodearray .str_set:N = \l_@@_decodearray_str , draft .bool_set:N = \l_@@_draft_bool , interpolate .bool_set:N = \l_@@_interpolate_bool , pagebox .choices:nn = { art , bleed , crop , media , trim } { \tl_set:Ne \l_@@_pagebox_tl { \l_keys_choice_tl box } } , pagebox .initial:n = crop , page .int_set:N = \l_@@_page_int , pdf-attr .str_set:N = \l_@@_pdf_str , type . str_set:N = \l_@@_type_str } % \end{macrocode} % \end{macro} % % \subsection{Obtaining bounding box data} % % \begin{variable} % { % \l_@@_llx_dim , \l_@@_lly_dim, % \l_@@_urx_dim , \l_@@_ury_dim % } % Storage for the return of bounding box. % \begin{macrocode} \dim_new:N \l_@@_llx_dim \dim_new:N \l_@@_lly_dim \dim_new:N \l_@@_urx_dim \dim_new:N \l_@@_ury_dim % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_bb_save:n, \@@_bb_save:e} % \begin{macro}{\@@_bb_restore:nF, \@@_bb_restore:eF} % Caching graphic bounding boxes is sensible, and these functions are needed both % here and for drive-specific work. So they are made available as documented % functions. To save on registers, the \enquote{origin} is only saved if it is % not at zero. % \begin{macrocode} \cs_new_protected:Npn \@@_bb_save:n #1 { \dim_if_exist:cTF { c_@@_ #1 _urx_dim } { \msg_error:nnn { graphic } { bb-already-cached } {#1} } { \dim_compare:nNnF \l_@@_llx_dim = { 0pt } { \dim_const:cn { c_@@_ #1 _llx_dim } { \l_@@_llx_dim } } \dim_compare:nNnF \l_@@_lly_dim = { 0pt } { \dim_const:cn { c_@@_ #1 _lly_dim } { \l_@@_lly_dim } } \dim_const:cn { c_@@_ #1 _urx_dim } { \l_@@_urx_dim } \dim_const:cn { c_@@_ #1 _ury_dim } { \l_@@_ury_dim } } } \cs_generate_variant:Nn \@@_bb_save:n { e } \cs_new_protected:Npn \@@_bb_restore:nF #1#2 { \dim_if_exist:cTF { c_@@_ #1 _urx_dim } { \dim_set_eq:Nc \l_@@_urx_dim { c_@@_ #1 _urx_dim } \dim_set_eq:Nc \l_@@_ury_dim { c_@@_ #1 _ury_dim } \dim_if_exist:cTF { c_@@_ #1 _llx_dim } { \dim_set_eq:Nc \l_@@_llx_dim { c_@@_ #1 _llx_dim } } { \dim_zero:N \l_@@_llx_dim } \dim_if_exist:cTF { c_@@_ #1 _lly_dim } { \dim_set_eq:Nc \l_@@_lly_dim { c_@@_ #1 _lly_dim } } { \dim_zero:N \l_@@_lly_dim } } {#2} } \cs_generate_variant:Nn \@@_bb_restore:nF { e } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_extract_bb:n, \@@_read_bb:n} % \begin{macro}{\@@_extract_bb_auix:nn, \@@_extract_bb_auix:Vn} % \begin{macro}{\@@_extract_bb_auxii:nnn} % \begin{macro}{\@@_extract_bb_auxiii:nnnn, \@@_extract_bb_auxiii:Vnnn} % \begin{macro}{\@@_extract_bb_auxiv:nnn} % \begin{macro}{\@@_read_bb_auxi:nnnn, \@@_read_bb_auxii:Vnnn} % \begin{macro} % { % \@@_read_bb_auxii:w , % \@@_read_bb_auxiv:w , % \@@_read_bb_auxv:w % } % Extracting the bounding box from an |.eps| or |.bb| file is done by % reading each line and searching for the marker text |%%BoundingBox:|. % The data is read as a string with a mapping over % the lines: as there is a colon involved, a little bit of work is needed to % get the matching correct. The same approach covers cases where the bounding % box has to be calculated by |extractbb|, with just the initial phase % different. % \begin{macrocode} \cs_new_protected:Npn \@@_extract_bb:n #1 { \int_compare:nNnTF \l_@@_page_int > 0 { \@@_extract_bb_auxi:Vn \l_@@_page_int {#1} } { \@@_extract_bb_auxii:nnn {#1} { } { } } } \cs_new_protected:Npn \@@_extract_bb_auxi:nn #1#2 { \@@_extract_bb_auxii:nnn {#2} { :P #1 } { -p~#1~ } } \cs_generate_variant:Nn \@@_extract_bb_auxi:nn { Vn } \cs_new_protected:Npn \@@_extract_bb_auxii:nnn #1#2#3 { \tl_if_empty:NTF \l_@@_pagebox_tl { \@@_extract_bb_auxiv:nnn } { \@@_extract_bb_auxiii:Vnnn \l_@@_pagebox_tl } {#1} {#2} {#3} } \cs_new_protected:Npn \@@_extract_bb_auxiii:nnnn #1#2#3#4 { \@@_extract_bb_auxiv:nnn {#2} { : #1 #3 } { #4 -B~#1~ } } \cs_generate_variant:Nn \@@_extract_bb_auxiii:nnnn { V } \cs_new_protected:Npn \@@_extract_bb_auxiv:nnn #1#2#3 { \@@_read_bb_auxi:nnnn {#1} {#2} { \ior_shell_open:Nn \l_@@_internal_ior { extractbb~#3-O~#1 } } { pipe-failed } } \cs_new_protected:Npn \@@_read_bb:n #1 { \@@_read_bb_auxi:nnnn {#1} { } { \ior_open:Nn \l_@@_internal_ior {#1} } { graphic-not-found } } % \end{macrocode} % Before any real searching, a check to see if there are cached values % available. The name of each graphic will be unique and so it's sensible to % store the bounding box data in \TeX{}: this avoids multiple file operations. % As bounding boxes here start away from the lower-left origin, we need to % store all four values (contrast with for example the \texttt{pdfmode} % driver). Here |#2| is a potential page identifier: used to allow caching of % individual pages in a multi-page document. Caching is applied to the % upper-right position in all cases, but as the lower-left will often be % $(0,0)$ it is only cached if required. % \begin{macrocode} \cs_new_protected:Npn \@@_read_bb_auxi:nnnn #1#2#3#4 { \@@_bb_restore:nF {#1#2} { \@@_read_bb_auxii:nnnn {#3} {#4} {#1} {#2} } } \cs_new_protected:Npe \@@_read_bb_auxii:nnnn #1#2#3#4 { #1 \exp_not:N \ior_if_eof:NTF \exp_not:N \l_@@_internal_ior { \msg_error:nnn { graphics } {#2} {#3} } { \ior_str_map_inline:Nn \exp_not:N \l_@@_internal_ior { \exp_not:N \@@_read_bb_auxiii:w ##1 ~ \c_colon_str \s_@@_stop } \@@_bb_save:n {#3#4} } \ior_close:N \exp_not:N \l_@@_internal_ior } \use:e { \cs_new_protected:Npn \exp_not:N \@@_read_bb_auxiii:w #1 \c_colon_str #2 \s_@@_stop { \exp_not:N \str_if_eq:nnT { \c_percent_str \c_percent_str BoundingBox } {#1} { \exp_not:N \@@_read_bb_auxiv:w #2 ( ) \s_@@_stop } } } % \end{macrocode} % If the bounding box is |atend|, just ignore the current one and keep going. % Otherwise, we need to allow for tabs and multiple spaces (as the line has % been read as a string). The easiest way to deal with that is to scan the % tokens: at this stage the line fragment should be just numbers and % whitespace. \TeX{} will then tidy up for us, with just a leading space to % worry about: that is taken out by the |\use:n| here. % \begin{macrocode} \cs_new_protected:Npn \@@_read_bb_auxiv:w #1 ( #2 ) #3 \s_@@_stop { \str_if_eq:nnF {#2} { atend } { \tl_set_rescan:Nne \l_@@_internal_tl { \char_set_catcode_space:n { 9 } \char_set_catcode_space:n { 32 } } { \use:n #1 } \exp_after:wN \@@_read_bb_auxv:w \l_@@_internal_tl \s_@@_stop } } % \end{macrocode} % A trailing space was deliberately added earlier so we know that the final % data point is terminated by a space. % \begin{macrocode} \cs_new_protected:Npn \@@_read_bb_auxv:w #1~#2~#3~#4~#5 \s_@@_stop { \dim_set:Nn \l_@@_llx_dim { #1 bp } \dim_set:Nn \l_@@_lly_dim { #2 bp } \dim_set:Nn \l_@@_urx_dim { #3 bp } \dim_set:Nn \l_@@_ury_dim { #4 bp } \ior_map_break: } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{variable}{\l_@@_final_name_str, \l_@@_full_name_str} % The full name is as you'd expect the name including path and extension. % The final name here reflects any conversions carried out by the backend, % for example if an |.eps| is converted to |.pdf|. % \begin{macrocode} \str_new:N \l_@@_final_name_str \str_new:N \l_@@_full_name_str % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_internal_box} % \begin{macrocode} \box_new:N \l_@@_internal_box % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_dir_str \l_@@_name_str \l_@@_ext_str} % \begin{macrocode} \str_new:N \l_@@_dir_str \str_new:N \l_@@_name_str \str_new:N \l_@@_ext_str % \end{macrocode} % \end{variable} % % \begin{variable}{\l_graphics_search_path_seq} % \begin{macrocode} \seq_new:N \l_graphics_search_path_seq % \end{macrocode} % \end{variable} % % \begin{variable}{\l_graphics_search_ext_seq} % Used to specify fall-back extensions: actually set on a per-backend basis. % \begin{macrocode} \seq_new:N \l_graphics_search_ext_seq % \end{macrocode} % \end{variable} % % \begin{variable}{\l_graphics_ext_type_prop} % Mapping between extensions and types for non-standard mappings % \begin{macrocode} \prop_new:N \l_graphics_ext_type_prop \prop_put:Nnn \l_graphics_ext_type_prop { .ps } { eps } % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_record_seq} % A list of graphic files used. % \begin{macrocode} \seq_new:N \g_@@_record_seq % \end{macrocode} % \end{variable} % % \begin{macro}{\graphics_include:nn, \graphics_include:nV} % \begin{macro}{\@@_include_search:n} % \begin{macro}{\@@_include:} % \begin{macro} % { % \@@_include_auxi:n, \@@_include_auxi:e, \@@_include_auxii:n, % \@@_include_auxiii:n, \@@_include_auxiv:n % } % Actually including an graphic is relatively straight-forward: most of the % work is done by the backend. We only have to deal with making sure the % box has no apparent depth. Where the first given name is not found, we % search based on extension only if the \meta{type} was not given. The one % wrinkle is that we may have found a \texttt{.tex} file matching the file % name stem: that's not what we want, so we have to filter out. % \begin{macrocode} \cs_new_protected:Npn \graphics_include:nn #1#2 { \group_begin: \keys_set:nn { graphics } {#1} \seq_set_eq:NN \l_file_search_path_seq \l_graphics_search_path_seq \file_get_full_name:nNTF {#2} \l_@@_full_name_str { \str_if_eq:eeTF { \l_@@_full_name_str } { #2 .tex } { \msg_error:nnn { graphics } { graphic-not-found } {#2} } { \@@_include: } } { \msg_error:nnn { graphics } { graphic-not-found } {#2} } \group_end: } \cs_generate_variant:Nn \graphics_include:nn { nV } \cs_new_protected:Npn \@@_include: { \str_if_empty:NTF \l_@@_type_str { \file_parse_full_name:VNNN \l_@@_full_name_str \l_@@_dir_str \l_@@_name_str \l_@@_ext_str \@@_include_auxi:e { \exp_args:Ne \str_tail:n { \str_casefold:V \l_@@_ext_str } } } { \@@_include_auxi:e { \l_@@_type_str } } } \cs_new_protected:Npn \@@_include_auxi:n #1 { \prop_get:NnNF \l_graphics_ext_type_prop { .#1 } \l_@@_internal_tl { \tl_set:Nn \l_@@_internal_tl {#1} } \exp_args:NV \@@_include_auxii:n \l_@@_internal_tl } \cs_generate_variant:Nn \@@_include_auxi:n { e } \cs_new_protected:Npn \@@_include_auxii:n #1 { \mode_leave_vertical: \cs_if_exist:cTF { @@_backend_include_ #1 :n } { \tl_set_eq:NN \l_@@_final_name_str \l_@@_full_name_str \str_set:Ne \l_@@_full_name_str { \exp_args:NV \__kernel_file_name_quote:n \l_@@_full_name_str } \exp_args:NnV \use:c { @@_backend_getbb_ #1 :n } \l_@@_full_name_str \seq_gput_right:NV \g_@@_record_seq \l_@@_final_name_str \clist_if_exist:NT \@filelist { \exp_args:NV \@addtofilelist \l_@@_final_name_str } \bool_if:NTF \l_@@_draft_bool { \@@_include_auxiii:n } { \@@_include_auxiv:n } {#1} } { \msg_error:nnn { graphics } { unsupported-graphic-type } {#1} } } \cs_new_protected:Npn \@@_include_auxiii:n #1 { \hbox_to_wd:nn { \l_@@_urx_dim - \l_@@_llx_dim } { \tex_vrule:D \tex_hss:D \vbox_to_ht:nn { \l_@@_ury_dim - \l_@@_lly_dim } { \tex_hrule:D width \dim_eval:n { \l_@@_urx_dim - \l_@@_llx_dim } \tex_vss:D \hbox_to_wd:nn { \l_@@_urx_dim - \l_@@_llx_dim } { \ttfamily \tex_hss:D \l_@@_full_name_str \tex_hss:D } \tex_vss:D \tex_hrule:D } \tex_hss:D \tex_vrule:D } } \cs_new_protected:Npn \@@_include_auxiv:n #1 { \hbox_set:Nn \l_@@_internal_box { \exp_args:NnV \use:c { @@_backend_include_ #1 :n } \l_@@_full_name_str } \box_set_dp:Nn \l_@@_internal_box { 0pt } \box_set_ht:Nn \l_@@_internal_box { \l_@@_ury_dim - \l_@@_lly_dim } \box_set_wd:Nn \l_@@_internal_box { \l_@@_urx_dim - \l_@@_llx_dim } \box_use_drop:N \l_@@_internal_box } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\graphics_show_list:, \graphics_log_list:, \@@_list:N} % \begin{macro}[EXP]{\@@_list_aux:n} % A function to list all graphic files used. % \begin{macrocode} \cs_new_protected:Npn \graphics_show_list: { \@@_list:N \msg_show:nneeee } \cs_new_protected:Npn \graphics_log_list: { \@@_list:N \msg_log:nneeee } \cs_new_protected:Npn \@@_list:N #1 { \seq_remove_duplicates:N \g_@@_record_seq #1 { kernel } { file-list } { \seq_map_function:NN \g_@@_record_seq \@@_list_aux:n } { } { } { } } \cs_new:Npn \@@_list_aux:n #1 { \iow_newline: #1 } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Utility functions} % % \begin{macro}{\graphics_get_full_name:nN} % \begin{macro}[TF]{\graphics_get_full_name:nN} % \begin{macro}{\@@_get_full_name:n} % As well as searching by path, etc., there is a need here to check that % we do not trip over |foo.bar| if |.bar| is not a known extension for % the current backend. % \begin{macrocode} \cs_new_protected:Npn \graphics_get_full_name:nN #1#2 { \graphics_get_full_name:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } } \prg_new_protected_conditional:Npnn \graphics_get_full_name:nN #1#2 { T , F , TF } { \group_begin: \seq_set_eq:NN \l_file_search_path_seq \l_graphics_search_path_seq \file_get_full_name:nNTF {#1} \l_@@_full_name_str { \str_if_eq:eeTF { \l_@@_full_name_str } { #1 .tex } { \@@_get_full_name:n {#1} } { \file_parse_full_name:VNNN \l_@@_full_name_str \l_@@_dir_str \l_@@_name_str \l_@@_ext_str \seq_map_inline:Nn \l_graphics_search_ext_seq { \str_if_eq:nVT {##1} \l_@@_ext_str { \seq_map_break:n { \use_none:nn } } } \@@_get_full_name:n {#1} } } { \@@_get_full_name:n {#1} } \exp_args:NNNV \group_end: \tl_set:Nn #2 \l_@@_full_name_str \tl_if_empty:NTF #2 { \prg_return_false: } { \prg_return_true: } } \cs_new_protected:Npn \@@_get_full_name:n #1 { \str_clear:N \l_@@_full_name_str \seq_map_inline:Nn \l_graphics_search_ext_seq { \file_get_full_name:nNT { #1 ##1 } \l_@@_full_name_str { \seq_map_break:n { \use_none:nn } } } \use:n { \str_clear:N \l_@@_full_name_str } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\graphics_get_pagecount:nN} % \begin{macro}{\@@_get_pagecount:n} % \begin{macro}{\@@_get_pagecount:nw} % A generic function to read the number of pages in a graphic file. This is % used by all of the backend where there is not a dedicated primitive. The % plan is essentially the same as reading the bounding box. To avoid multiple % calls, the value is cached either here or in the backend. % \begin{macrocode} \cs_new_protected:Npn \graphics_get_pagecount:nN #1#2 { \group_begin: \seq_set_eq:NN \l_file_search_path_seq \l_graphics_search_path_seq \file_get_full_name:nNTF {#1} \l_@@_full_name_str { \int_if_exist:cF { c_@@_ \l_@@_full_name_str _pages_int } { \exp_args:NV \@@_backend_get_pagecount:n \l_@@_full_name_str } \tl_set:Nv #2 { c_@@_ \l_@@_full_name_str _pages_int } } { \tl_set:Nn #2 { 0 } \msg_error:nnn { graphics } { graphic-not-found } {#1} } \exp_args:NNNV \group_end: \tl_set:Nn #2 #2 } \cs_new_protected:Npe \@@_get_pagecount:n #1 { \ior_shell_open:Nn \exp_not:N \l_@@_internal_ior { extractbb~-O~#1 } \exp_not:N \ior_if_eof:NTF \exp_not:N \l_@@_internal_ior { \msg_error:nnn { graphics } { pipe-failed } } { \ior_str_map_inline:Nn \exp_not:N \l_@@_internal_ior { \exp_not:N \@@_get_pagecount:nw {#1} ##1 ~ \c_colon_str \c_colon_str \s_@@_stop } \exp_not:N \int_if_exist:cF { c_@@_ #1 _pages_int } { \int_const:cn { c_@@_ #1 _pages_int } { 1 } } } \ior_close:N \exp_not:N \l_@@_internal_ior } \use:e { \cs_new_protected:Npn \exp_not:N \@@_get_pagecount:nw #1#2 \c_colon_str #3 \c_colon_str #4 \s_@@_stop { \exp_not:N \str_if_eq:nnT { \c_percent_str \c_percent_str Pages } {#2} { \int_const:cn { c_@@_ #1 _pages_int } {#3} \exp_not:N \ior_map_break: } } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Messages} % % \begin{macrocode} \msg_new:nnnn { graphics } { graphic-not-found } { Image~file~'#1'~not~found. } { LaTeX~tried~to~open~graphic~file~'#1',~ but~the~file~could~not~be~read. } \msg_new:nnnn { graphics } { pipe-failed } { Cannot~run~piped~system~commands. } { LaTeX~tried~to~call~a~system~process~but~this~was~not~possible.\\ Try~the~"--shell-escape"~(or~"--enable-pipes")~option. } \msg_new:nnnn { graphics } { unsupported-graphic-type } { Image~type~'#1'~not~supported~by~current~driver. } { LaTeX~was~asked~to~include~an~graphic~of~type~'#1',~ but~this~is~not~supported~by~the~current~driver~(production~route). } % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex