I have 4 photos that I would like to place in in grid on my pages and add a caption to each photo. Does anyone know how to do this?
[Tex/LaTex] Place a grid of pictures (2×2) with captions
graphicssubfloats
Related Solutions
This works for me:
\documentclass{article}
\usepackage{amsmath}
\newcounter{eqn}
\renewcommand*{\theeqn}{\alph{eqn})}
\newcommand{\num}{\refstepcounter{eqn}\text{\theeqn}\;}
\makeatletter
\newcommand{\putindeepbox}[2][0.7\baselineskip]{{%
\setbox0=\hbox{#2}%
\setbox0=\vbox{\noindent\hsize=\wd0\unhbox0}
\@tempdima=\dp0
\advance\@tempdima by \ht0
\advance\@tempdima by -#1\relax
\dp0=\@tempdima
\ht0=#1\relax
\box0
}}
\makeatother
\usepackage[demo]{graphicx}
% \setlength{\tabcolsep}{0pt}
\begin{document}
\begin{tabular}{cc}
\num\putindeepbox[7pt]{\includegraphics{whatever.jpg}}
& \num\putindeepbox[7pt]{\includegraphics{whatever.jpg}} \\
\num\putindeepbox[7pt]{\includegraphics{whatever.jpg}}
& \num\putindeepbox[7pt]{\includegraphics{whatever.jpg}} \\
\end{tabular}
\end{document}
(Edited to include pseudo-captions; the macro \putindeepbox
takes something and puts it into a box of height equal to 0.7\baselineskip
by default (i.e., the height of a \strut
) and deep enough to accomodate for the given material. Somehow in tabular
\baselineskip
gets reset to zero, so I've hardcoded the height manually. Not very elegant, but works - feel free to improve it;). Notice also some dirty tricks with boxes - hopefully someone will find a cleaner way to do it. I hope that this is what you wanted, and that it is not too late... See also comments below to make spacing better.)
The spacing is not perfect; you can manipulate the horizontal spacing with tabcolsep
, AFAIK there is no such parameter for vertical spacing in tabular.
This is not complete answer to this question, it's rather showcase of idea I had about generative layouts and luatex
. I don't think that any of following examples is useful as is,
but maybe in can serve as basis for more interesting solutions in the future, maybe something like this.
My idea is following: we have LaTeX command that stores it's content in a box ant then add list id and dimensions to the list.
\ProvidesPackage{generativelayout}
\RequirePackage{luacode}
\luaexec{%
generators = require('generativelayout')
genboxes = {}
curr_box_id = 2222
function findFreeBox(id)
if type(tex.box[id]) == 'nil' then
return id
else
return findFreeBox(id + 1)
end
end
gen = generators.getGenerator("default")
}
\newcommand\gensavebox[1]{%
\setbox0=\hbox{\fbox{#1}}
\luaexec{%
local box = tex.box[0]
curr_box_id= findFreeBox(curr_box_id)
tex.setbox('global',curr_box_id, node.copy_list(box))
table.insert(genboxes,{width=box.width / 65536 ,height=(box.height / 65536 + box.depth / 65536),box=curr_box_id})
curr_box_id = curr_box_id + 1
}
%\box0
}
Next we have environment genlayout
that selects layout algorithm and run it on list of boxes:
\newenvironment{genlayout}[1][]{%
\luaexec{%
local gen_name = "\luatexluaescapestring{#1}"
if gen_name == "" then gen_name = "default" end
gen = generators.getGenerator(gen_name)
gen:init()
}
}{%
\luaexec{%
gen:run(genboxes)
}
}
This package can be used like this:
\documentclass{article}
\usepackage{lmodern,generativelayout}
\begin{document}
\pagestyle{empty}
\begin{genlayout}[]
\gensavebox{%
Hello world
}
\gensavebox{%
\begin{tabular}{l l}
hello & world\\
second & line\\
\end{tabular}
}
\gensavebox{%
\begin{minipage}{.3\textwidth}
\begin{itemize}
\item First
\item Second
\end{itemize}
\end{minipage}
}
\gensavebox{%
little bit smaller box
}
\gensavebox{totaly small box}
\gensavebox{azsvm}
\end{genlayout}
\end{document}
Now the lua library generativelayout.lua
module(...,package.seeall)
generators = {}
empty_generator = {init = function() end, run = function() end}
function getGenerator(name)
return generators[name] or empty_generator
end
printBox = function(self,box_number)
tex.print('\\fbox{\\copy'..box_number.."}")
end
generators.default = {
init = empty_generator.init,
printer = printBox,
run = function(self,boxes)
for k, v in pairs(boxes) do
self:printer(v.box)
end
end
}
Every generator is added as object with methods run and init, which are called form genlayout
environment. Generator can be selected with optional parameter of genlayout
, if empty, default
is selected. The output is following:
We can sort the boxes:
generators.bigToSmall = {
init = generators.default.init,
printer = printBox,
run = function(self,boxes)
table.sort(boxes,function(a, b) return a.height > b.height end)
generators.default.run(self,boxes)
end
}
You can use functions from other generators, as generators.default.run(self,boxes)
More funny example of putting boxes on random places on the page:
generators.randomize = {
width = 150,
height = 150,
init = generators.default.init,
printer = function(self,box,x,y)
tex.print("\\put(",x,",",y,"){\\copy",box,"}")
end,
run = function(self,boxes)
tex.print("\\setlength\\unitlength{1pt}")
tex.print("\\begin{picture}("..self.width..","..self.height..")")
math.randomseed( os.time() )
for k,v in pairs(boxes) do
local x = math.random(self.width - v.width)
local y = math.random(self.height - v.height)
self:printer(v.box,x,y)
end
tex.print("\\end{picture}")
end
}
Here we use trick with picture
environment to place things on exact place on the page
And finally, my port of http://codeincomplete.com/posts/2011/5/7/bin_packing/ to lua:
generators.binPack = {
root = {},
init = function(self) self.root = {x=0,y=0,w=280,h=250} end,
printer = generators.randomize.printer,
fit = function(self,boxes)
local node
for _,block in ipairs(boxes) do
print("Velikost boxu",block.width,block.height)
node = self:findNode(self.root, block.width,block.height)
if node then
block.fit = self:splitNode(node, block.width, block.height)
end
end
end,
findNode = function(self,root,w,h)
if root.used then
local right = self:findNode(root.right,w,h)
if right then
return right
else
return self:findNode(root.down,w,h)
end
elseif w <= root.w and h <= root.h then
return root
else
return nil
end
end,
splitNode = function (self,node,w,h)
print("Split box: ",node.x,node.y,node.w,node.h,w,h)
node.used = true
node.down = { x= node.x, y= node.y + h, w= node.w, h= node.h - h }
node.right = { x= node.x + w, y= node.y, w= node.w - w, h= h}
return node
end,
run = function(self,boxes)
--generators.randomize.run(self,boxes)
table.sort(boxes,function(a, b) return a.height > b.height end)
tex.print("\\setlength\\unitlength{1pt}")
tex.print("\\begin{picture}("..self.root.w..","..self.root.h..")")
self:fit(boxes)
for _,v in ipairs(boxes) do
if v.fit then
x= v.fit.x
y= self.root.h-v.fit.y
self:printer(v.box,x,y)
end
end
tex.print("\\end{picture}")
end
}
I don't think the result is really nice and I think there is some error in placing boxes on their exact place, but maybe someone will like it:
Related Question
- [Tex/LaTex] Placing several pictures in one line, but with linebreak
- [Tex/LaTex] Arranging multiple plots in a grid inside a figure/subfloat
- [Tex/LaTex] Subfigures in a columnwide figure in a two-column document
- [Tex/LaTex] Combine a grid of images into a single figure WITHOUT subfigures and labels, captions, etc
Best Answer
Use the
subfig
package.