我想用 LaTeX 创建“年鉴风格”的照片布局。每张照片都会有一个标题,目标是自动布局不同大小的矩形照片,以尽量减少页面上的空白(每张照片和标题周围都有强制性边距)。如果我也可以用浏览器做到这一点,那也会很有帮助,但这是另一个论坛的问题。我原本以为这个问题可以通过某种开源软件包得到解决,但我没有找到。
答案1
这不是对这个问题的完整回答,而是我对生成布局的想法的展示luatex
。我认为以下任何示例都没有用,但也许可以作为未来更有趣的解决方案的基础,也许是这样的这。
我的想法如下:我们有 LaTeX 命令,将其内容存储在一个盒子中,然后将列表 ID 和尺寸添加到列表中。
\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
}
接下来我们有genlayout
选择布局算法的环境并在框列表上运行它:
\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)
}
}
这个包可以这样使用:
\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}
现在lua库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
}
每个生成器都以对象的形式添加,并带有方法 run 和 init,这两个方法由genlayout
环境调用。生成器可以使用可选参数 进行选择genlayout
,如果为空,则default
选择 。输出如下:
我们可以对这些框进行排序:
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
}
您可以使用其他生成器的函数,例如generators.default.run(self,boxes)
将框放置在页面随机位置的更多有趣示例:
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
}
在这里我们使用picture
环境技巧将事物放置在页面上的准确位置
最后,我的端口http://codeincomplete.com/posts/2011/5/7/bin_packing/到 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
}
我认为结果并不是很好,并且我认为将盒子放置在其准确位置存在一些错误,但也许有人会喜欢它: