TeX - LaTeX Asked on May 24, 2021
I have two problems whilst formatting a two-column index with subitems:
documentclass{scrartcl}
usepackage{imakeidx}
usepackage[indentunit=15pt,justific=raggedright, columnsep=10pt, font=footnotesize]{idxlayout}
makeindex
begin{document}
Some text.
index{Donahue!Alice}
index{Donahue!Barney}
index{Jones!Alvin}
index{Jones!Billy}
index{Jones!Carter}
index{Jones!Dennis}
index{Smith!Alex}
index{Smith!Brian}
index{Smith!Carrie}
index{Smith!Dewey}
index{Smith!Eric}
index{Smith!Frank}
index{Smith!Gary}
index{Smith!Huey}
index{Smith!Irma}
index{Smith!Joey}
index{Smith!Kevin}
index{Scullion!Alex}
index{Scullion!Brian}
index{Scullion!Carrie}
index{White!Alice}
index{White!Barney}
idxlayout{columns=2, font=small, columnsep=20pt}
printindex
end{document}
In this MWE (1) the index-entry »Smith« is cut off from its sub-entries and (2) the space between the groups (entry + sub-entry) is uneven.
My two questions: Would it be possible to (1) fix the space between the item-groups and also (2) implement an algorithm that makes the entry stick together with at least one (or two, or three) sub-entries in the same column?
For the MWE this would result in the right column being a little shorter than the left one. »Alex, 1« would be pulled towards »Smith«. In my opinion a little better that what is the case now.
You can redefine @idxitem
(which item
is let equal to inside theindex
) to do some bookkeeping, namely to reset a counter that each subsequent subitem
would decrease.
Also subitem
adds a penalty when the value of the counter is positive, so a page/column break is prohibited.
documentclass{scrartcl}
usepackage{imakeidx}
usepackage[indentunit=15pt,justific=raggedright, columnsep=10pt, font=footnotesize]{idxlayout}
makeindex
makeatletter
letori@idxitem@idxitem
def@idxitem{clear@penaltiesori@idxitem}
defclear@penalties{subitem@count=3 }
newcountsubitem@count
defsubitem{%
advancesubitem@count -1
par
ifnumsubitem@count>0 penalty10000 fi
ori@idxitemhspace*{ila@subindent}%
}
makeatother
begin{document}
Some text.
index{Donahue!Alice}
index{Donahue!Barney}
index{Jones!Alvin}
index{Jones!Billy}
index{Jones!Carter}
index{Jones!Dennis}
index{Smith!Alex}
index{Smith!Brian}
index{Smith!Carrie}
index{Smith!Dewey}
index{Smith!Eric}
index{Smith!Frank}
index{Smith!Gary}
index{Smith!Huey}
index{Smith!Irma}
index{Smith!Joey}
index{Smith!Kevin}
index{Scullion!Alex}
index{Scullion!Brian}
index{Scullion!Carrie}
index{White!Alice}
index{White!Barney}
idxlayout{columns=2, font=small, columnsep=20pt}
printindex
end{document}
This will keep two subitems together with the main entry. With
defclear@penalties{subitem@count=2 }
you'd get just one subitem attached.
Correct answer by egreg on May 24, 2021
The vertical spacing is different because the blank space is only inserted when there is a new letter (according to this documentation). Since you dislike it, I am going to fix it anyways.
I tried to write the solution in LaTeX3, but somehow regular expressions do not work in Boolean expressions. For convenience, I am using Lua instead. That is to say, the following code only runs in LuaLaTeX. The idea is simple: read the ind
file generated by makeindex
and fine-tune it based on our purposes.
You may need to compile the document twice for it work properly.
documentclass{scrartcl}
usepackage{imakeidx}
usepackage[indentunit=15pt,justific=raggedright, columnsep=10pt, font=footnotesize]{idxlayout}
usepackage{etoolbox}
usepackage{luacode}
usepackage{expl3}
usepackage{hyperref}
makeindex
begin{luacode*}
local inspect = require"inspect"
function fine_tune_index()
local index_filename = tex.jobname .. ".ind"
local newindex_filename = tex.jobname .. "-new.ind"
local attr = lfs.attributes(index_filename)
if attr == nil then
return
end
local index_lines = {}
for line in io.lines(index_filename) do
table.insert(index_lines, line)
end
local new_lines = {}
local new_line_buffer = {}
local line=nil
local has_space=true
local search_end = #index_lines - 1
local item_count = 0
function flush_line_buffer()
if #new_line_buffer > 1 then
table.insert(new_line_buffer, 1, [[begin{minipage}{linewidth}]])
table.insert(new_line_buffer, 4, [[end{minipage}]])
end
for _, item in ipairs(new_line_buffer) do
table.insert(new_lines, item)
end
new_line_buffer = {}
end
for i=2,search_end do
line = index_lines[i]
if line:find("item") then
item_count = item_count + 1
flush_line_buffer()
if not has_space then
table.insert(new_lines, "indexspace")
end
has_space = false
table.insert(new_line_buffer, line)
elseif line:find("indexspace") then
has_space=true
flush_line_buffer()
table.insert(new_lines, line)
else
table.insert(new_line_buffer, line)
end
end
flush_line_buffer()
table.insert(new_lines, 1, index_lines[1])
table.insert(new_lines, index_lines[#index_lines])
local out_text = table.concat(new_lines, "n")
local outfile = io.open(newindex_filename, "w")
outfile:write(out_text)
outfile:close()
end
end{luacode*}
AtEndDocument{
directlua{fine_tune_index()}
}
begin{document}
Some text.
index{Donahue!Alice}
index{Donahue!Barney}
index{Jones!Alvin}
index{Jones!Billy}
index{Jones!Carter}
index{Jones!Dennis}
index{Smith!Alex}
index{Smith!Brian}
index{Smith!Carrie}
index{Smith!Dewey}
index{Smith!Eric}
index{Smith!Frank}
index{Smith!Gary}
index{Smith!Huey}
index{Smith!Irma}
index{Smith!Joey}
index{Smith!Kevin}
index{Scullion!Alex}
index{Scullion!Brian}
index{Scullion!Carrie}
index{White!Alice}
index{White!Barney}
idxlayout{columns=2, font=small, columnsep=20pt}
ExplSyntaxOn
% create a new int variable to bypass checks
int_new:c {jobname-new@idxfile}
ExplSyntaxOff
printindex[jobname-new]
end{document}
Answered by Alan Xiang on May 24, 2021
Ok, here's a LaTeX solution.
Unfortunately, the idxlayout
package doesn't offer a lot of hooks to make this kind of modifications, so my changes are somewhat invasive and will break if you use different layout options.
If you run into problems, the following could be made a bit more generic.
You should put this code into the preamble, for instance after loading idxlayout
. What it does is redefine the required internals from idxlayout
to make the desired changes. I tried to be as minimally invasive as possible, but meh.
% Could put this into a separate package which loads idxlayout
makeatletter
% no additional space before new letters
letindexspacerelax
% rescue original definition
letorig@idxitem@idxitem
% we need to know if a subitem is the first after an item
newififafteritem
% item should do what it originally does, plus add vertical space,
% plus set the flag for subitem
renewcommand{@idxitem}{%
orig@idxitem
vspace{ila@initsep}%
afteritemtrue
}
% subitem should refer to original definition of @idxitem (to avoid
% adding vertical space), plus add penalty if it's the first subitem.
renewcommand{subitem}{%
orig@idxitem
ifafteritemnobreakfi
hspace*{ila@subindent}%
afteritemfalse
}%
renewcommand{subsubitem}{orig@idxitemhspace*{ila@subsubindent}}%
makeatother
Result:
Answered by Stephan Lehmke on May 24, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP