42

I have a set of pictures in my working directory and I want to make a PDF report using these images. I want to include all of them, but the number of the pictures changes in each case. I would like to make a "for" loop to insert each picture one after the other sequentially. Can anyone help me? At the moment the code I have is the following:

\begin{figure}[h!]
\begin{centering}
\includegraphics[width=10cm,height=10cm]{Figure1.png}
\caption[Sèries temporals de vent i direccions]{Sèries temporals velocitat de vent i     direccions. filtratge de pics i glaçades de velocitat.}
\label{Serie}
\end{centering}

\end{figure}

I would like to do it for every .png image in the directory. Thanks for your questions: I am using Windows 7 and the file names follow the pattern Figura1.png, Figura2.png and so forth. So which of these possibilities would fit best?

3
  • 1
    I don't know of an (easy) way that can traverse a directory from within LaTeX and apply a certain command for each file. There is PerlTeX, wich might be the easiest-to-use candidate for this job within LaTeX. However, PerlTeX is as far as I know not installed by default (in common LaTeX distros such as TexLive or MikTeX), and if you use PerlTex, you have to invoke LaTeX with some special flags. If you, however, do it "outside" from LaTeX, i.e. you set up a script that generates that very part of your document automatically, than you have to run this script and afterwards you can call LaTeX.
    – phimuemue
    Apr 26, 2012 at 10:59
  • Are you on Windows, or Unix (Mac/Linux)? Are the files named in a systematic format? If the files have a controlled naming format you could consider using How to iterate through the name of files in a folder Apr 26, 2012 at 16:37

5 Answers 5

35

Suppose your pictures are all following certain naming convention (here I used pic1.png up to pic4.png), the lipsumpackage is just for the dummy text:

\documentclass[parskip]{scrartcl}
\usepackage[margin=10mm,a6paper]{geometry}
\usepackage{tikz}
\usepackage{lipsum}

\begin{document}

\foreach \x in {1,2,3,4}
{ \lipsum[\x]
    \includegraphics[scale=1]{pic\x.png}
    \clearpage
}

\end{document}

Alternatively, you can specify tuples of variable to iterate over. The following does the same:

\foreach \x/\picname in {4/pic1.png,8/pic2.png,15/pic3.png,16/pic4.png}
{ \lipsum[\x]
    \begin{figure}[htb]
        \includegraphics[scale=1]{\picname}
        \caption{The very nice picture \x}
    \end{figure}
    \clearpage
}

And the output being:

enter image description here

1
26

Using the wonderful python.sty package (see github) and some embedded python code, this is very easy:

\documentclass{article}

\usepackage{python}
\usepackage{graphicx}

\begin{document}

\begin{python}
import os
directory = "."
extension = ".png"
files = [file for file in os.listdir(directory) if file.lower().endswith(extension)]

for file in files:
   print r"\begin{figure}[!ht]"
   print r"\centering"
   print r"\includegraphics[width=10cm,height=10cm]{%s}" % file
   print r"\caption{File %s}" % file
   print r"\label{Serie}"
   print r"\end{figure}"
\end{python}

\end{document}

Notice that you need to compile with the --enable-write18 option in order to run the python code.

8
  • 3
    It might be simpler to generate the code with python (or whatever softaware or language you used to generate the figures) using a similar syntax and copy and paste it. I often do it with R and MATLAB.
    – M. Toya
    Apr 26, 2012 at 19:05
  • Does it runs under Windows 7 too? Could you explain me this sentence please? files = [file for file in os.listdir(directory) if file.lower().endswith(extension)]
    – JPV
    Apr 28, 2012 at 9:23
  • @Jonel_R: the line files = [file for file in os.listdir(directory) if file.lower().endswith(extension)] first constructs the list os.listdir(directory) and then filters it by the condition file.lower().endswith(extension).
    – yori
    Apr 28, 2012 at 19:02
  • @Jonel_R: Yes, from now on, the github version of python.sty should work on Windows too.
    – yori
    Apr 29, 2012 at 3:16
  • 3
    The PythonTeX package is another Python option. It works very well under Windows. It doesn't need write18, only runs Python code when it is modified, and provides more ways to use Python-generated content. The example above would work with PythonTeX, except you would need \usepackage{pythontex} and would need to use the pycode environment to wrap the Python code.
    – G. Poore
    Aug 31, 2012 at 17:49
13

If your system is a Unix one, the following set of macros will do, but they require that the file is compiled with pdflatex -shell-escape:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[catalan]{babel}
\usepackage{graphicx}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\figureloop}{m}
 {
  \group_begin:
  \etex_everyeof:D { \exp_not:N }
  \tex_endlinechar:D \c_minus_one
  \seq_gset_split:Nnx \g_figloop_names_seq { , }
    { \tex_input:D |"ls ~ -m ~ #1*.png" ~}
  \group_end:
  \seq_map_inline:Nn \g_figloop_names_seq { \dofigureloop { ##1 } }
 }
\cs_generate_variant:Nn \seq_gset_split:Nnn {Nnx}
\seq_new:N \g_figloop_names_seq
\ExplSyntaxOff

\NewDocumentCommand \dofigureloop {m}
 {
  \begin{figure}
  \centering
  \includegraphics[width=10cm,height=10cm]{#1}
  \caption[Sèries temporals de vent i direccions]
          {Sèries temporals velocitat de vent i direccions. Filtratge de pics i glaçades de velocitat.}
  \label{Serie-#1}
  \end{figure}
 }

\begin{document}

\figureloop{figure}

\end{document}

The argument to \figureloop is the common part in the names of your figures. It might be generalized to different extensions.

The \tex_input:D |"ls~-m~#1*.png"~ bit loads the output of the ls command sent via shell-escape to the operating system. This output is stored in a sequence that is then split. Then a mapping function is called on the sequence, its argument is exactly a file name and the macro called just typesets the necessary figure environment. A label in the form Serie-<filename> is also generated.

With a different shell command this may be adapted also to Windows systems.


A simpler version, that requires -shell-escape anyway; using kpsewhich should ensure it can be executed as is also on Windows systems.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[catalan]{babel}
\usepackage{graphicx}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\figureloop}{m}
 {
  \jonel_figureloop:n { #1 }
 }

\ior_new:N \g_jonel_ls_stream

\seq_new:N \l_jonel_filenames_seq

\cs_new_protected:Npn \jonel_figureloop:n #1
 {
  \seq_clear:N \l_jonel_filenames_seq
  \ior_open:Nn \g_jonel_ls_stream { "|kpsewhich ~ #1*.png" }
  \ior_map_inline:Nn \g_jonel_ls_stream
   { \seq_put_right:Nf \l_jonel_filenames_seq { \tl_trim_spaces:n { ##1 } } }
  \seq_map_inline:Nn \l_jonel_filenames_seq { \dofigureloop { ##1 } }
 }
\cs_generate_variant:Nn \seq_put_right:Nn {Nf}
\ExplSyntaxOff

\NewDocumentCommand \dofigureloop {m}
 {
  \begin{figure}
  \centering
  \includegraphics[width=10cm,height=10cm]{#1}
  \caption[Sèries temporals de vent i direccions]
          {Sèries temporals velocitat de vent i direccions. 
           Filtratge de pics i glaçades de velocitat.}
  \label{Serie-#1}
  \end{figure}
 }


\begin{document}

% assume all figures are in the "figures" subfolder
% and are named "figureXXX.png"

\figureloop{figures/figure}

\end{document}

Update 2019

Due to changes in expl3, the previous solution will no longer work. Here's a new version.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[catalan]{babel}
\usepackage{graphicx}
\usepackage{xparse}
\usepackage{showlabels}

\ExplSyntaxOn
\NewDocumentCommand{\figureloop}{O{png}mm}
 {
  \jonel_figureloop:nnn { #1 } { #2 } { #3 }
 }


\tl_new:N \l_jonel_filenames_tl
\seq_new:N \l_jonel_filenames_seq
\cs_new_protected:Nn \__jonel_filenames_process:nn {}
\cs_generate_variant:Nn \__jonel_filenames_process:nn { VV }

\cs_new_protected:Nn \jonel_figureloop:nnn
 {
  \sys_shell_get:nnN { kpsewhich ~ #2*.#1 } {} \l_jonel_filenames_tl
  \seq_set_split:NnV \l_jonel_filenames_seq { ~ } \l_jonel_filenames_tl
  \seq_pop_right:NN \l_jonel_filenames_seq \l_tmpa_tl
  \cs_set_protected:Nn \__jonel_filenames_process:nn { #3 }
  \seq_map_variable:NNn \l_jonel_filenames_seq \l__jonel_filenames_fullname_tl
   {
    \tl_set_eq:NN \l__jonel_filenames_shortname_tl \l__jonel_filenames_fullname_tl
    \tl_remove_once:Nn \l__jonel_filenames_shortname_tl { ./#2 }
    \tl_remove_once:Nn \l__jonel_filenames_shortname_tl { .#1 }
    \__jonel_filenames_process:VV \l__jonel_filenames_fullname_tl \l__jonel_filenames_shortname_tl
   }
 }
\ExplSyntaxOff

\begin{document}

% assume all figures are in the "figures" subfolder
% and are named "figureXXX.png"

\figureloop{figures/ScreenShot}{
  \begin{figure}
  \centering
\showtokens{\#1=#1,\#2=#2}
  \includegraphics[width=10cm,height=10cm,keepaspectratio]{#1}
  \caption[Sèries temporals de vent i direccions]
          {Sèries temporals velocitat de vent i direccions. 
           Filtratge de pics i glaçades de velocitat.}
  \label{Serie-#2}
  \end{figure}
 }


\end{document}

enter image description here

1
  • Due to internal changes in expl3, the method in the “simpler version” will no longer work and the first one should not be used.
    – egreg
    Jun 6, 2019 at 8:46
10

Edit:

I want to expand my first answer with an easy to understand approach based on LaTeX3 (pdflatex). Because it follows the same principle like the LuaLaTeX answer I gave (see for explanation), I don't want to separate it from my original answer. So one can compare the two solutions and see how easy it is to use some basics of LaTeX3. As in the LuaLaTeX solution one is independent from external scripting languages, shell commands and specific operating systems.

\documentclass{article}
\usepackage{expl3}
\usepackage{graphicx}

\ExplSyntaxOn

% new bool for the loop condition
\bool_new:N \runWhileLoop    
% new integer for the filename counter
\int_new:N \counter

% create a new latex command
% #1 filename base
% #2 file extension
\newcommand{\figureLoop}[2]
{   
    % set the condition to 'true'
    \bool_set_true:N \runWhileLoop        
    % set the counter to '0'
    \int_zero:N \counter

    % run the loop until '\runWhileLoop' is set to 'false' 
    \bool_while_do:Nn {\runWhileLoop}
    { 
        % increment the counter   
        \int_incr:N \counter            
        % merge the given arguments to a filename
        \edef\fileName{#1\int_use:N\counter#2}

        % check if the file exists
        \file_if_exist:nTF {\fileName}
        {            
            % file exists
            \begin{figure}
                \centering
                \includegraphics[width=5cm]{\fileName}
                \caption{Filename:~\fileName}
                \label{fig:\fileName}
            \end{figure}
        }{
            % there is no file with the given name
            % break the loop
            \bool_set_false:N \runWhileLoop
        }    
    }
}

\ExplSyntaxOff

\begin{document}
\figureLoop{figure}{.jpg}
\figureLoop{image}{.png}
\end{document}

Original:

I think this is a good job for LuaLaTeX. You are independent of the operating system and any external scripts. In this example there is a lua function which concatenates a filename, checks if the file exists and put the picture to the 'figure' environment. You don't have to know how many files are in the folder and you can reuse the given function with different (base)filenames and extensions.

It is good practice to write the lua functions in a separate file with the extension .lua. For this MWE I use the filecontents environment to do so.

\documentclass{book}
\usepackage{graphicx}
\usepackage{filecontents}

%create a lua script file
\begin{filecontents*}{luaFunctions.lua}
function PrintImages(name, extension)

    local i = 1
    local filename = ""

    while true do    
        --concatenate the filename
        filename = name..i..extension
        i = i + 1

        if FileExists(filename) then
            tex.print(string.format("\\begin{figure}[h!]"))
            tex.print(string.format("\\begin{centering}"))

            tex.print(string.format("\\includegraphics[width=15cm, height=10cm]{"..filename.."}"))
            tex.print(string.format("\\caption{"..filename.."}"))
            tex.print(string.format("\\label{fig:"..filename.."}"))

            tex.print(string.format("\\end{centering}"))
            tex.print(string.format("\\end{figure}"))
        else
            --if the file doesn't exists break the while loop
            break
        end        
    end
end

--checks if a file exists
function FileExists(fileName)
    -- try to open the file
    local file = io.open(fileName, "r")

    -- if the file exists close the file and return true
    if file ~= nil then
        io.close(file)
        return true
    else
        return false
    end
end
\end{filecontents*}

% read the external lua file to declare the functions,
% but without execute the Lua commands and functions
\directlua{dofile("luaFunctions.lua")}

% latex commands to execute the lua functions
\def\printImages#1#2{\directlua{PrintImages("#1", "#2")}}

\begin{document}
%choose what type of images you want to print
\printImages{figure}{.jpg}
\printImages{image}{.png}
\end{document}
7

Compile the following with pdflatex -shell-escape filename.tex. Only for Windows users.

% filename.tex

\documentclass[preview,border=12pt,varwidth]{standalone}
\usepackage{graphicx}
\edef\subdir{"Sub Dir/"}

\graphicspath{{\subdir}}
\immediate\write18{cmd /c dir /b \subdir\space *.png > imagelist.txt}

\begin{document}
\makeatletter
\newread\reader
\openin\reader=imagelist.txt\relax
\begingroup
\endlinechar=-1\relax
\loop
    \readline\reader to \data
    \unless\ifeof\reader
            \filename@parse{\data}  
            \section{\filename@base.\filename@ext}
            \includegraphics[scale=.5,ext=.\filename@ext]{"\filename@base"}\endgraf
\repeat
\endgroup
\closein\reader
\makeatother
\end{document}

Features

  • It can accept spaces in the file name or path.
  • It can import images from any sub directory.
1
  • what a great answer ! Thank you.
    – JeT
    Jul 28, 2020 at 21:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.