2

This problem is driving me nuts. I'd like to change the includegraphics path depending on some conditions. It seems simple, right?

I already read the answers to this question: Passing image path to \includegraphics using a macro, but I was unable to solve my issue. I learned that for \includegraphics the following must be true:

Thanks David Carlisle -> Incorrect Statement: From my understanding, my code only requires one level of expansion, but is still not working.

Is this a rare time when I need to use \DeclareExpandableDocumentCommand for \qikpicpath?

Code

A prerequisite is having a jpg file on directory above the tex file called test_pic.jpg. I commended out the problematic code.

\documentclass{article}
\usepackage{fontspec}
\newcommand{\qikrootdirectory}{../}
\usepackage{xparse}
\usepackage{xstring}
\usepackage{tikz}

\ExplSyntaxOn % https://tex.stackexchange.com/a/275552/13552
\tl_new:N \g_qik_pic_class_tl % allocate a (global) variable
\tl_gset:Nn \g_qik_class_tl { I } % initialize the value
\NewDocumentCommand{\qikpicclass}{} % define a user level command for printing the value
    {
    \tl_use:N \g_qik_class_tl
    }
\NewDocumentCommand{\qiksetpicclass}{m} % define a user level command for changing the value
    {
    \tl_gset:Nn \g_qik_class_tl { #1 }
    }
\NewDocumentCommand{\qikpicpath}{}
    {
    \tl_if_eq:VnTF \g_qik_class_tl { I }
        { \qikrootdirectory~test_pic.jpg } % true
        { \qikrootdirectory~test_pic.jpg } % false
    }
\cs_generate_variant:Nn \tl_if_eq:nnTF { V }
\ExplSyntaxOff

\begin{document}
\qikpicpath % Visually inspect expanded text.

\includegraphics[width=\textwidth]{\qikrootdirectory test_pic.jpg}% Works

%\includegraphics[width=\textwidth]{\qikpicpath}% Expansion Problem

\tikz \node (pic) {\includegraphics[width=\textwidth]{../test_pic.jpg}};% Works

%\tikz \node (pic) {\includegraphics[width=\textwidth]{\qikpicpath}};% Expansion Problem Related to Above

\end{document}
  • 3
    No ~ space before test_pic! – egreg Oct 29 '15 at 15:33
  • @egreg I am removed both ~ spaces, but my output after uncommenting the first commented out line is ! You can't use \the letter I' after \the. ` – Jonathan Komar Oct 29 '15 at 15:54
  • @egreg I should also note that I use a space in that location in LaTeX2e code. – Jonathan Komar Oct 29 '15 at 16:08
  • it isn't clear what problem you are trying to solve, it is usually best not to have a path anyway but here since the file extension is not hidden in the macro the comments about expansion in the referenced question do not apply at all. \def\qikrootdirectory{../} should work if ../ works (security settings in texmf.cnf might prevent reading files in a path starting ../) – David Carlisle Oct 29 '15 at 16:50
  • @DavidCarlisle Haha I suspected that my understanding was completely off. You just confirmed it. So the expansion takes this course: \includegraphics>\qikpicpath>\tl_if_eq:VnTF \g_qik_class_tl { I }>\jkorootdirectory? Is that right? How is it not clear what problem I am trying to solve? The path is most definitely required in my case. The entire SVN system depends on paths. – Jonathan Komar Oct 29 '15 at 16:56
  • @macmadness86 obviously the image needs to be at some path but you don't normally have to specify the path in your document. You don't specify the full path to article.cls in every document for example. If TeX can find article.cls it can find your jpg files. – David Carlisle Oct 29 '15 at 17:03
  • @DavidCarlisle My understanding was that LaTeX could only find files within the scope of the local tex file and the installed folders /usr/bin/texlive. If multiple documents in separate dirs under a common directory structure must access the same picture, it makes sense to have them located higher in the tree and shared amongst docs (save resources). Multiple users can check out a fully working dir tree from the revision control system without admin rights and everything should compile. How else would I tell LaTeX where the global objects are (objs. because there are other global things). – Jonathan Komar Oct 29 '15 at 17:13
  • if you can input it at all you can add that directory to your TEXINPUTS variable (or use \graphicspath) and then \includegraphics{test_pic} will work. – David Carlisle Oct 29 '15 at 17:17
  • @DavidCarlisle Thanks for the tip. I knew about that. However, editing TEXINPUTS would make it work on my computer while breaking paths on other machines using fresh revision copies (svn checkout). Also, there is a document that is so large that the actual root directory is reset such that each chapter can be compiled locally as individual documents. The server takes each chapter and comments out the appropriate lines, merges the chapters, and changes the root directory. It generally works quite well, providing excellent revision control. I just run into annoying snags with \includegraphics. – Jonathan Komar Oct 29 '15 at 17:32
  • @macmadness86 I'd use TEXINPUTS personally but if you want to use tex macros to configure it I can't see why \graphicspath doesn't do what you need why do you have to hide the filename in a macro as you comment there is no problem using a macro top hold the path. – David Carlisle Oct 29 '15 at 17:52
  • @DavidCarlisle Well TEXINPUTS is off the table for the aforementioned reason. We keep the tex install on all machines as vanilla as possible for maintenance reasons (ease, predictability, upgradability). As for \graphicspath, that could be an option if \graphicspath{\qikrootdirectory} would work. There is a caveat to this method, however. It will introduce a constraint on the filenames. No local image may have the same name as a global image. I do not know why anybody would intentionally do such a thing, but it would be a restriction that I currently do not have. – Jonathan Komar Oct 29 '15 at 18:45
  • \graphicspath{{\qikrootdirectory}} – David Carlisle Oct 29 '15 at 22:23

2 Answers2

3

This could be a case where \DeclareExpandableDocumentCommand turns out to be useful, but it's simpler to use a different approach.

Let's say you have the same images in color and b/w versions, in two different folders, say ./colorimages and ./bwimages. Your purpose is to load pictures from one folder or another in case you compile your PDF for viewing or printing.

Let's also say that the images have consistent names like color-image1.jpg and bw-image1.jpg, so you want to do

\mmincludegraphics[width=.5\textwidth]{image1}

and this will load the right image, either color or b/w, just with one switch to be set in the preamble.

\ExplSyntaxOn

\NewDocumentCommand{\mmincludegraphics}{O{}m}
 {
  \includegraphics[#1]{
    \g_macmadness_folder_tl/ % the folder
    \g_macmadness_prefix_tl % the prefix
    #2
  }
 }

\NewDocumentCommand{\setimagefolder}{m}
 {
  \str_case:nn { #1 }
   {
    {color}
     {
      \tl_gset:Nn \g_macmadness_folder_tl { ./colorimages }
      \tl_gset:Nn \g_macmadness_prefix_tl { color- }
     }
    {bw}
     {
      \tl_gset:Nn \g_macmadness_folder_tl { ./bwimages }
      \tl_gset:Nn \g_macmadness_prefix_tl { bw- }
     }
   }
 }

\tl_new:N \g_macmadness_folder_tl
\tl_new:N \g_macmadness_prefix_tl

\ExplSyntaxOff

Now you can state in the preamble

\setimagefolder{color}

or

\setimagefolder{bw}

and call, in the document, \mmincludegraphics as stated before.

egreg
  • 1,121,712
  • Hm interesting approach. My situation is possibly easier in that all shared images are in a common location. I will need to consider whether introducing a new \mmincludegraphics command just for one graphic is a wise option. Another less clean (duplicate code) approach I've considered is putting the \includegraphics command within conditional logic and hardcoding the image filenames in each condition. One complication: I would have to be able to access the optional parameters [width=,height=] of \includegraphics. – Jonathan Komar Oct 29 '15 at 18:57
  • Sorry, but I don't understand: all the work is for a single image? – egreg Oct 29 '15 at 19:04
  • No, I mistakenly wrote "one graphic". I meant one graphic case. e.g. img-label-with-cert.jpg and img-label.jpg are in a common folder above the tex file's dir, but their names could be anything (but likely similar). On the contrary, all the work is for potentially many images. As this project is for a very expensive product, I try to only use scalable options. – Jonathan Komar Oct 29 '15 at 19:12
  • 1
    @macmadness86 It doesn't work like this: please, present your real problem with all specifications. The previous question had no image; this one has one image; the comment tells that there is one, but it can be many. I can't follow you. – egreg Oct 29 '15 at 19:16
  • Hang on. Your example is very much on track! The only difference is that I might have to switch between 3 images instead of 2. The real problem was in the question all along: How can I use a LaTeX3 variable as a path in includegraphics (solving the expansion problem)? I have a path expansion problem. Doesn't that cover it? None of the answers regarding \includegraphics seem to hit the nail on the head. I've had a long day at work and I am prob. not thinking clearly anymore. I'll be back… Thx for your help. – Jonathan Komar Oct 29 '15 at 19:24
  • @macmadness86 Two or three, with \str_case:nn is immaterial. For the expansion problem, the main fact is that \tl_if_eq:nn is not expandable. But a redefinition of \includegraphics is needed anyway. – egreg Oct 29 '15 at 20:28
  • Good to know about \tl_if_eq:nn. Could you confirm that the reason your code works is because \includegraphics sees \g_ith_folder_tl/ and \g_ith_suffix_tl and expands them one time and the result is the path that needs no further expansion? The reason I am confused is that I replaced ./colorimages in \tl_gset:Nn \g_macmadness_folder_tl { ./colorimages } with \qikrootdirectory and it worked! This baffles me, because doesn't that mean that \g_ith_folder_tl/ must be expanded multiple times? If \includegraphics can only handle one expansion, then why does this work here? – Jonathan Komar Oct 30 '15 at 08:06
  • 1
    @macmadness86 If I use \str_if_eq:VnTF instead of \tl_if_eq:VnTF (no variant generation is necessary and \DeclareExpandableDocumentCommandfor\qikpicpath, it works. When\includegraphics` processes the file name, it performs full expansion. – egreg Oct 30 '15 at 09:41
2

If you add

{\tracingmacros1
\setbox0\hbox{\qikpicpath}
}

Then TeX traces the expansion steps of this macro:

\qikpicpath ->\tl_if_eq:VnTF \g_qik_class_tl {I}{\qikrootdirectory test_pic.jpg
}{\qikrootdirectory test_pic.jpg}

\tl_if_eq:VnTF ->\exp_args:NV \tl_if_eq:nnTF 

\exp_args:NV #1#2->\exp_after:wN #1\exp_after:wN {\exp:w \__exp_eval_register:N
 #2}
#1<-\tl_if_eq:nnTF 
#2<-\g_qik_class_tl 

\__exp_eval_register:N #1->\exp_after:wN \if_meaning:w \exp_not:N #1#1\if_meani
ng:w \scan_stop: #1\__exp_eval_error_msg:w \fi: \else: \exp_after:wN \use_i_ii:
nnn \fi: \exp_after:wN \exp_end: \tex_the:D #1
#1<-\g_qik_class_tl 

\use_i_ii:nnn #1#2#3->#1#2
#1<-\exp_after:wN 
#2<-\exp_end: 
#3<-\tex_the:D 

\g_qik_class_tl ->I

\tl_if_eq:nnTF #1#2->\group_begin: \tl_set:Nn \l__tl_internal_a_tl {#1}\tl_set:
Nn \l__tl_internal_b_tl {#2}\if_meaning:w \l__tl_internal_a_tl \l__tl_internal_
b_tl \group_end: \prg_return_true: \else: \group_end: \prg_return_false: \fi: \
exp_end: 
#1<-I
#2<-I

\tl_set:Nn #1#2->\cs_set_nopar:Npx #1{\exp_not:n {#2}}
#1<-\l__tl_internal_a_tl 
#2<-I

\tl_set:Nn #1#2->\cs_set_nopar:Npx #1{\exp_not:n {#2}}
#1<-\l__tl_internal_b_tl 
#2<-I

\prg_return_true: ->\exp_after:wN \use_i:nn \exp:w 

\use_i:nn #1#2->#1
#1<-\qikrootdirectory test_pic.jpg
#2<-\qikrootdirectory test_pic.jpg

\qikrootdirectory ->../

Counting -> shows that it takes twelve expansions to get ../ in this case.

It would still work with includegraphics if you use the type and read keys to tell it that it is a jpg file, but usually this is the wrong approach.

David Carlisle
  • 757,742
  • Amatuer question: Where do I put \tracingmacros1? Should the output be on the pdf or in the log? Also, I copied your post into a file called file and ran grep -E '\->' file | wc -l to make sure my eyes weren't going blind and the result was 12, not 5. I suppose the likely explanation is that I do not understand what you have posted. – Jonathan Komar Oct 30 '15 at 08:15
  • 1
    sorry 5/12 was just silliness on my part, it seems I can't count:-) If you add the three lines I show anywhere in the document then the log file should have the trace I show, \tracingmacros is an integer register so it's just a normal tex assignment, so you can put it anywhere but if you don't limit the scope with {..} then you get a lot of trace output in the log as you trace ever command. – David Carlisle Oct 30 '15 at 08:25
  • No problem. I suppose 12 expansions is too much for \includegraphics, hm? :-) Thank you for the comment about the internal registers-normal tex assignment. – Jonathan Komar Oct 30 '15 at 08:28
  • @macmadness86 you can have 10000 expansions in the path part but if you want it to see the .jpg and so automatically detect the image type and use the right back end code you have to not hide that. If you do hide it you need to use type= and the other little used attributes on \includegraphics to give the information in other ways. But I can't see why you'd need to hide the file name. – David Carlisle Oct 30 '15 at 08:33