% Author        : C. Pierquet
% licence       : Released under the LaTeX Project Public License v1.3c or later, see http://www.latex-project.org/lppl.txt

\NeedsTeXFormat{LaTeX2e}
\ProvidesExplPackage{inlinegraphicx}{2026-10-03}{0.20d}{Insert inline graphicx with LaTeX3}

%====HISTORY
% v 0.20d  Macro for path & graphicspath & [inherit=false] option
% v 0.20c  Macro for params
% v 0.20b  Placeholder if file not found
% v 0.20a  Bugfix with gset + l3 improvements
% v 0.2.0  LaTeX3 conversion
% v 0.1.1  Alt options for includegraphics
% v 0.1.0  Initial version

%====PACKAGE
\RequirePackage { graphicx }

%====MESSAGES
\msg_new:nnn { inlinegraphicx } { file-not-found }
  {
    Image~'#1'~not~found~;~inserting~a~dummy~image~instead.
  }

%====OPTION
\DeclareKeys [ inlinegraphicx ]
  {
    inherit .bool_gset:N = \g_inlinegraphicx_inherit_bool ,
    inherit .default:n   = true ,
  }
\SetKeys [ inlinegraphicx ] { inherit = true }
\ProcessKeyOptions [ inlinegraphicx ]

%====VARS
\dim_new:N \l_totheight_inlinegraphicx_dim
\dim_new:N \l_depth_inlinegraphicx_dim
\dim_new:N \l_raisebox_inlinegraphicx_dim
\dim_new:N \l_height_inlinegraphicx_dim
\dim_new:N \l_dummyinner_inlinegraphicx_dim
\dim_new:N \l_dummydiag_inlinegraphicx_dim
\dim_new:N \l_dummyht_inlinegraphicx_dim
\dim_new:N \l_dummydp_inlinegraphicx_dim
\dim_new:N \l_dummyraise_inlinegraphicx_dim
\fp_new:N  \l_scale_inlinegraphicx_fp
\fp_new:N  \l_invscale_inlinegraphicx_fp
\tl_new:N  \l_strut_inlinegraphicx_tl
\tl_new:N  \l_dummy_inlinegraphicx_tl
\tl_new:N  \l_resolved_inlinegraphicx_tl
\clist_new:N \l_extensions_inlinegraphicx_clist
\clist_new:N \l_searchpath_inlinegraphicx_clist
\bool_new:N \l_inlinegraphicx_exists_bool
 
%====KEYS
\keys_define:nn { inlinegraphics }
  {
    scale .fp_set:N          = \l_scale_inlinegraphicx_fp,
    scale .initial:n         = { 1.0 },
    scale .default:n         = { 1.0 },
    strut .tl_set:N          = \l_strut_inlinegraphicx_tl,
    strut .initial:n         = { qH },
    strut .default:n         = { qH },
    dummy .tl_set:N          = \l_dummy_inlinegraphicx_tl,
    dummy .initial:n         = { },
    extensions .clist_set:N  = \l_extensions_inlinegraphicx_clist,
    extensions .initial:n    = { .pdf, .png, .jpg, .jpeg, .eps },
    searchpath .clist_set:N  = \l_searchpath_inlinegraphicx_clist,
    searchpath .initial:n    = { },
  }

%====HOOK INHERIT
\AtBeginDocument
  {
    \bool_if:NT \g_inlinegraphicx_inherit_bool
      {
        \cs_if_exist:NT \Ginput@path
          {
            \clist_clear:N \l_searchpath_inlinegraphicx_clist
            \tl_set:No \l_tmpa_tl { \Ginput@path }
            \tl_map_function:NN \l_tmpa_tl
              \inlinegraphicx_inherit_add:n
          }
      }
  }

%====MACRO CONFIG GLOBAL
\NewDocumentCommand \inlinegraphicxsetup { m }
  { \keys_set:nn { inlinegraphics } { #1 } }

%====MACRO SEARCHPATH(s)
\NewDocumentCommand \inlinegraphicspath { m }
  {
    \graphicspath { #1 }
    \clist_clear:N \l_searchpath_inlinegraphicx_clist
    \tl_map_inline:nn { #1 }
      { \clist_put_right:Nn \l_searchpath_inlinegraphicx_clist { ##1 } }
  }

%====INTERNAL FUNCTIONS
\cs_new_protected:Npn \inlinegraphicx_inherit_add:n #1
  { \clist_put_right:Nn \l_searchpath_inlinegraphicx_clist { #1 } }

\cs_new_protected:Npn \inlinegraphicx_check_exist:n #1
  {
    \bool_set_false:N \l_inlinegraphicx_exists_bool
    \tl_set:Nn \l_resolved_inlinegraphicx_tl { #1 }
    % 1) raw name
    \file_if_exist:nT { #1 }
      {
        \bool_set_true:N \l_inlinegraphicx_exists_bool
        \tl_set:Nn \l_resolved_inlinegraphicx_tl { #1 }
      }
    % 2) name + ext
    \bool_if:NF \l_inlinegraphicx_exists_bool
      {
        \clist_map_inline:Nn \l_extensions_inlinegraphicx_clist
          {
            \file_if_exist:nT { #1 ##1 }
              {
                \bool_set_true:N \l_inlinegraphicx_exists_bool
                \tl_set:Nn \l_resolved_inlinegraphicx_tl { #1 ##1 }
                \clist_map_break:
              }
          }
      }
    % 3) searchfolder + name (+ ext)
    \bool_if:NF \l_inlinegraphicx_exists_bool
      {
        \clist_map_inline:Nn \l_searchpath_inlinegraphicx_clist
          {
            \file_if_exist:nT { ##1 #1 }
              {
                \bool_set_true:N \l_inlinegraphicx_exists_bool
                \tl_set:Nn \l_resolved_inlinegraphicx_tl { ##1 #1 }
                \clist_map_break:
              }
            \bool_if:NF \l_inlinegraphicx_exists_bool
              {
                \clist_map_inline:Nn \l_extensions_inlinegraphicx_clist
                  {
                    \file_if_exist:nT { ##1 #1 ####1 }
                      {
                        \bool_set_true:N \l_inlinegraphicx_exists_bool
                        \tl_set:Nn \l_resolved_inlinegraphicx_tl { ##1 #1 ####1 }
                        \clist_map_break:
                      }
                  }
              }
            \bool_if:NT \l_inlinegraphicx_exists_bool { \clist_map_break: }
          }
      }
    \bool_if:NF \l_inlinegraphicx_exists_bool
      { \msg_warning:nnn { inlinegraphicx } { file-not-found } { #1 } }
  }
 
\cs_new_protected:Npn \inlinegraphicx_dummybox:
  {
    \dim_set:Nn \l_dummyinner_inlinegraphicx_dim
      { \l_height_inlinegraphicx_dim - 1.2pt }
    \dim_set:Nn \l_dummydiag_inlinegraphicx_dim
      { \fp_eval:n { 1.41421356 * \l_dummyinner_inlinegraphicx_dim } pt }
    \fboxsep 0pt \fboxrule 0.6pt
    \hbox_set:Nn \l_tmpb_box
      {
        \fbox
          {
            \makebox [ \dim_use:N \l_dummyinner_inlinegraphicx_dim ] [ c ]
              {
                \makebox [ 0pt ] [ c ]
                  { \rotatebox 
                      [ origin = c ]
                      { 45  }
                      { 
                        \rule 
                          { \dim_use:N \l_dummydiag_inlinegraphicx_dim }
                          { 0.5pt } 
                      } 
                  }
                \makebox [ 0pt ] [ c ]
                  { \rotatebox
                      [ origin = c ]
                      { -45 }
                      { 
                        \rule 
                          { \dim_use:N \l_dummydiag_inlinegraphicx_dim }
                          { 0.5pt }
                      } 
                  }
              }
          }
      }
    % v alignement
    \dim_set:Nn \l_dummyht_inlinegraphicx_dim { \box_ht:N \l_tmpb_box }
    \dim_set:Nn \l_dummydp_inlinegraphicx_dim { \box_dp:N \l_tmpb_box }
    \dim_set:Nn \l_dummyraise_inlinegraphicx_dim
      {
        0.5 \l_height_inlinegraphicx_dim
        -
        0.5 \l_dummyht_inlinegraphicx_dim
        +
        0.5 \l_dummydp_inlinegraphicx_dim
      }
    \hbox_set:Nn \l_tmpb_box
      {
        \raisebox 
          { \dim_use:N \l_dummyraise_inlinegraphicx_dim } 
          { \box_use:N \l_tmpb_box } 
      }
    \box_set_ht:Nn \l_tmpb_box { \l_height_inlinegraphicx_dim }
    \box_set_dp:Nn \l_tmpb_box { 0pt }
    \box_use:N \l_tmpb_box
  }
 
\cs_new_protected:Npn \inlinegraphicx_insert:nn #1 #2
  {
    \bool_if:NTF \l_inlinegraphicx_exists_bool
      {
        \includegraphics
          [ height = { \dim_use:N \l_height_inlinegraphicx_dim } , #1 ]
          { \l_resolved_inlinegraphicx_tl }
      }
      {
        \tl_if_empty:NTF \l_dummy_inlinegraphicx_tl
          { \inlinegraphicx_dummybox: }
          {
            \includegraphics
              [ height = { \dim_use:N \l_height_inlinegraphicx_dim } , #1 ]
              { \l_dummy_inlinegraphicx_tl }
          }
      }
  }
 
%====MAIN MACRO
\NewDocumentCommand \inlinegraphics { s O { } D < > { } m }
{
  \group_begin:
 
  \keys_set:nn { inlinegraphics }
    {
      scale = { 1.0 } ,
      strut = { qH } ,
      dummy = { } ,
    }
  \keys_set:nn { inlinegraphics } { #2 }
 
  \inlinegraphicx_check_exist:n { #4 }
 
  % ---
  \bool_if:NTF #1
  {
    % --- starred : height = strut height
    \hbox_set:Nn \l_tmpa_box { \tl_use:N \l_strut_inlinegraphicx_tl }
    \dim_set:Nn \l_totheight_inlinegraphicx_dim { \box_ht:N \l_tmpa_box }
    \dim_set:Nn \l_height_inlinegraphicx_dim
      {
        \fp_eval:n
        {
          \l_scale_inlinegraphicx_fp * \l_totheight_inlinegraphicx_dim
        }
        pt
      }
    \inlinegraphicx_insert:nn { #3 } { #4 }
  }
  {
    % --- non starred : totheight (ht+dp) with v alignement
    \hbox_set:Nn \l_tmpa_box { \tl_use:N \l_strut_inlinegraphicx_tl }
    \dim_set:Nn \l_totheight_inlinegraphicx_dim
      { \box_dp:N \l_tmpa_box + \box_ht:N \l_tmpa_box }
    \dim_set:Nn \l_depth_inlinegraphicx_dim
      { \box_dp:N \l_tmpa_box }
    \dim_set:Nn \l_height_inlinegraphicx_dim
      {
        \fp_eval:n
        {
          \l_scale_inlinegraphicx_fp * \l_totheight_inlinegraphicx_dim
        }
        pt
      }
    \fp_set:Nn \l_invscale_inlinegraphicx_fp
      { 0.5 * ( 1.0 - \l_scale_inlinegraphicx_fp ) }
    \dim_set:Nn \l_raisebox_inlinegraphicx_dim
      {
        \l_depth_inlinegraphicx_dim
        -
        \fp_eval:n
        {
          \l_invscale_inlinegraphicx_fp * \l_totheight_inlinegraphicx_dim
        }
        pt
      }
 
    \raisebox { - \dim_use:N \l_raisebox_inlinegraphicx_dim }
      { \inlinegraphicx_insert:nn { #3 } { #4 } }
  }
  \group_end:
}

%====SIMPLE WRAPPER
\NewDocumentCommand \safeincludegraphics { O { } m }
{
  \group_begin:
  \inlinegraphicx_check_exist:n { #2 }
  \bool_if:NTF \l_inlinegraphicx_exists_bool
    { \includegraphics [ #1 ] { \l_resolved_inlinegraphicx_tl } }
    { \includegraphics [ #1 ] { example-image.pdf } }
  \group_end:
}

\endinput