1

Time for revenge! I've been fighting with this problem of aligning items according to the longest label automatically for some time now (see first two examples below). The left side of the longest label matches the beginning of the paragraphs (\parindent). It's not common situation but it looks fine in documents I polish. The authors use it for abbreviations of hypothesis, conditions, properties, theorems, you name it.

The common option is to set up the type of item manually by \begin{itemize}[Example a)] via the enumerate package. Or with the help of \leftskip dimension written directly into an environment.

In production I hadn't used LuaTeX (callback) as I wasn't able to program it. I probably could transform that environment into a table, but I would expect a lot of troubles. I wasn't aware of the environ package at that time, therefore I used a heavy hammer:

  1. At a Lua level I select itemize environments one by one from a TeX file (regular expressions).
  2. Then I select all the labels from the \item[label] commands and create a new TeX file with a series of \measureme{label} commands (Lua).
  3. I load that file by an external TeX file (\input) and find the longest label plus I store that dimension via \message command in the log file.
  4. I parse that log file by Lua and I add \leftskip to that itemize environment before the first occurence of \item[].

It's rather slow, ineffective, but it works. I care about the first level itemize, not about higher levels as they are rare in documents I polish (in other words there is no need to add \leftskip there).

This procedure doesn't handle nested itemize well, but I could improve it by deleting inner itemize environments after Step 1 once I handle balanced itemize selection (e.g. by using LPeg or replacing \begin{itemize} and \end{itemize} by a single unused character, e.g. \002 and \003, I successfully tried that for the first time here). After that step, only \items of the actual level of itemize remain untouched for further analysis. That's my ToDo.

I was thinking if there is an easier way how to handle this alignment. This is my try with the help of environ package. The idea is to process the content of itemize twice:

  • During the first run the algorithm should measure labels and pick up the longest one (virtual typesetting in a box).
  • During the second run the algorithm should use \leftskip to alter environment (real typesetting).

I enclose a minimal working example. It would suit me well. It processes the first level of itemize as shown in the example. But we are getting error messages if we try to process nested environments. Please comment out the first \end{document} command to see a minimal non-working example (I turned on \nonstopmode to get some PDF). I was thinking to add \ifnum and a level of itemize, but I haven't tried that, yet. We run any LaTeX engine, e.g. lualatex mal-example.tex.

There might be completely different approach from mine how to handle this sort of alignment. Is there any?

% *latex mal-example.tex
\documentclass[a4paper]{article}
\pagestyle{empty}
\usepackage{environ}

\let\olditemize=\itemize % original \begin{itemize}
\let\endolditemize=\enditemize % original \end{itemize}
\let\olditem=\item % original \item command

\newdimen\measureme % for storing maximum width of item labels
\RenewEnviron{itemize}{% redefinition of itemize environment
   \measureme=0mm% set maximum width to zero, initial value
   \def\item[##1]{\setbox0=\hbox{##1}% redefinition of \item
       \ifdim\wd0>\measureme % getting the maximum value
          \global\measureme=\wd0 % store the width maximum
          \fi % End of \ifdim...
       }% End of redefinition of \item.
       \setbox1=\hbox{\BODY}% measure labels virtually
       \setbox1=\hbox{\enspace}% measuring space
       \advance\measureme by -\wd1\relax % width of space
   \let\item=\olditem% Back to common \item.
   \begin{olditemize}% common itemize + \leftskip - width of space
       \leftskip=\measureme % aproach I use  
       \BODY % common typesetting of \items...
   \end{olditemize}
   }% End of redefinition of the itemize environment.

\begin{document}
Text before. Text before. Text before. Text before. Text before. Text before. Text before. Text before. Text before. Text before. Text before. Text before. Text before. Text before.
\begin{itemize}
\item[(malabc)] First item.
\item[(longer one)] Second item.
\item[(3rd)] Third item.
\end{itemize}

Text in the middle. Text in the middle. Text in the middle. Text in the middle. Text in the middle. Text in the middle. Text in the middle. 
\begin{itemize}
\item[(cond1)] First item.
\item[(cond11)] Second item.
\item[(cond111)] Third item.
\end{itemize}

\end{document} % to be commented out
Additional text. Additional text. Additional text. Additional text. Additional text. Additional text. Additional text. Additional text. Additional text. Additional text. 

\nonstopmode
\begin{itemize}
\item[(a)] First item.
\item[(b)] Second item.
\begin{itemize}
\item[(cba)] First item.
\item[(abc)] Second item.
\item[(cab)] Third item.
\end{itemize}
\item[(c)] Third item.
\end{itemize}

\end{document}

Three examples: 2 are correct, the last one is incorrect

  • For multiple layers, you will need a different \measureme for each layer, or at least a \bgroup \egroup to create a new local variable for \measureme each time. enumitem uses \@itemdepth, but the base itemize doesn't (as far as I can tell). – John Kormylo Apr 9 '15 at 18:19

Your Answer

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

Browse other questions tagged or ask your own question.