TransWikia.com

How can I modify the environment of enumerate to put every item in a minipage?

TeX - LaTeX Asked by haff on April 6, 2021

I’m writing a little exam generation package in R that takes a list of questions, randomizes question order and responses, and creates a .pdf. One issue I have is that questions and their responses may be separated by a page break, which is obviously not ideal. I’ve found a manual workaround in encapsulating each item within a minipage, but a manual solution won’t work here.

Is it possible to modify the environment of enumerate or create a custom command that puts each item in a minipage to keep questions from being split across pages? That way, I can include the code in the preamble of the RMarkdown document that generates the exam.

Essentially I want

documentclass{article}

begin{document}

begin{enumerate}
item
  Which bear is best?

  A. Honey bear
  B. Grizzly bear
  C. Black bear
  D. Teddy bear
  E. Polar bear
item
  How much snow per year is the preferable amount?

  A. 90 inches
  B. 120+ inches
  C. 30 inches
  D. 60 inches
end{enumerate}
end{document}

to become

documentclass{article}

begin{document}

begin{enumerate}
begin{minipage}{linewidth}
item
  Which bear is best?

  A. Honey bear
  B. Grizzly bear
  C. Black bear
  D. Teddy bear
  E. Polar bear
end{minipage}
begin{minipage}{linewidth}
item
  How much snow per year is the preferable amount?

  A. 90 inches
  B. 120+ inches
  C. 30 inches
  D. 60 inches
end{minipage}
end{enumerate}
end{document}

by putting something in the preamble.

4 Answers

You can do something like

newcommand{question}[1]{
  begin{minipage}{linewidth}
  item
    {#1}
  end{minipage}
}

So the full code would be

documentclass{article}

begin{document}

newcommand{question}[1]{
  begin{minipage}{linewidth}
  item
    {#1}
  end{minipage}
}

begin{enumerate}
  question{Which bear is best?A. Honey bearB. Teddy bearC. Black bearD. Grizzly bearE. Polar bear}
end{enumerate}

end{document}

Answered by haff on April 6, 2021

Here's a LuaLaTeX-based solution. It sets up a Lua function, called add_minipage_wrappers, that acts as a preprocessor of sorts: It scans all input lines, kicks into high gear when it encounters a begin{enumerate} instruction, encases item groups in minipage environments, and falls back to near-dormancy after it encounters an end{enumerate} instruction. The Lua function doesn't operate on item directives contained in itemize or description list environments.

The only input requirements are as follows:

  1. the instructions begin{enumerate} and end{enumerate} occur at the beginning of lines
  2. no lines contain both a begin{enumerate} and an item instruction
  3. only one item instruction on a given line
  4. enumerate environments aren't nested.

I trust that these input requirements aren't too burdensome.

Here's what the output looks like if the Lua function is active. To deactivate the function, just comment out (or delete) the lines

AtBeginDocument{directlua{luatexbase.add_to_callback (
   "process_input_buffer" , add_minipage_wrappers , "addminipagewrappers" )}}

enter image description here

% !TEX TS-program = lualatex
documentclass{article}
pagestyle{empty}
usepackage[margin=5mm,textheight=0.5cm,paperheight=4.5cm]{geometry}

usepackage{luacode} %% for 'luacode*' environment
begin{luacode*}
local in_enumerate = false -- define a couple of flags (Boolean variables)
local in_item = false
function add_minipage_wrappers ( s )
  if s:find ( "^begin{enumerate}" ) then
     in_enumerate = true -- switch 'in_enumerate' to "true"
  elseif s:find ( "^end{enumerate}" ) then
     s = s:gsub ( "end{enumerate}" , "end{minipage} end{enumerate}" )
     in_enumerate = false
     in_item = false
  elseif s:find ( "item" ) then
     if in_enumerate == true then  -- do nothing unless in an 'enumerate' env.
        if in_item == false then   -- first instance of 'item' in the list
           s = s:gsub ( "item" , "begin{minipage}{linewidth} item" )
           in_item = true          -- switch 'in_item' to "true"
        else                       -- 2nd or later instance of 'item' 
           s = s:gsub ( "item" , "end{minipage} begin{minipage}{linewidth} item" )
        end
     end  
  end
  return ( s )
end
end{luacode*}
%% Assign the Lua function to LuaTeX's 'process_input_buffer' callback:
AtBeginDocument{directlua{luatexbase.add_to_callback (
   "process_input_buffer" , add_minipage_wrappers , "addminipagewrappers" )}}

begin{document}
begin{enumerate}
item
  Which bear is best?

  A. Honey bear
  B. Grizzly bear
  C. Black bear
  D. Teddy bear
  E. Polar bear
item
  How much snow per year is the preferable amount?

  A. 90 inches
  B. 120+ inches
  C. 30 inches
  D. 60 inches
end{enumerate}
end{document}

Answered by Mico on April 6, 2021

You can capture the items and process them using the approach provided by Macro to capture until end-of-line as argument:

enter image description here

documentclass{article}

%usepackage{xparse}% If LaTeX < 2020-10

ExplSyntaxOn
NewDocumentEnvironment{myenumerate}{ O{} +b }
 {
  % do the setup
  keys_set:nn { haff/enumerate } { #1 }
  % split the contents at item
  seq_set_split:Nnn l_haff_enumerate_input_seq { item } { #2 }
  % remove the first (empty) item
  seq_pop_left:NN l_haff_enumerate_input_seq l_tmpa_tl
  % issue the preamble
  tl_use:N l_haff_enumerate_pre_tl
  % adorn the items
  seq_set_map:NNn
    l_haff_enumerate_output_seq
    l_haff_enumerate_input_seq
    { exp_not:n { __haff_enumerate_do:n { ##1 } } }
  % output the items, separated by the chosen separator
  seq_use:NV l_haff_enumerate_output_seq l_haff_enumerate_sep_tl
  % issue the postamble
  tl_use:N l_haff_enumerate_post_tl
 }
 {}

seq_new:N l_haff_enumerate_input_seq
seq_new:N l_haff_enumerate_output_seq
cs_generate_variant:Nn seq_use:Nn { NV }

keys_define:nn { haff/enumerate }
 {
  pre    .tl_set:N  = l_haff_enumerate_pre_tl,
  post   .tl_set:N  = l_haff_enumerate_post_tl,
  sep    .tl_set:N  = l_haff_enumerate_sep_tl,
  action .code:n    = cs_set_eq:NN __haff_enumerate_do:n #1,
  action .initial:n = use:n,
 }
ExplSyntaxOff

NewDocumentCommand{minipageitem}{+m}{%
  parnoindent
  begin{minipage}{linewidth}
    setcounter{option}{0}%
    item #1
  end{minipage}
}
NewDocumentEnvironment{minipageenumerate}{O{}}{%
  begin{myenumerate}[
    action=minipageitem,
    pre=begin{enumerate},
    post=end{enumerate}
  ]
}{%
  end{myenumerate}
}

newcounter{option}
renewcommand{theoption}{Alph{option}}
newcommand{option}{%
  parnoindent
  refstepcounter{option}%
  theoption.~ignorespaces
}

begin{document}

begin{minipageenumerate}
  item
    Which bear is best?

    option Honey bear
    option Grizzly bear
    option Black bear
    option Teddy bear
    option Polar bear
    
  item
    How much snow per year is the preferable amount?

    option 90 inches
    option 120+ inches
    option 30 inches
    option 60 inches
end{minipageenumerate}

end{document}

Answered by Werner on April 6, 2021

You can redefine the existing enumerate environment using the tasks package. The latter already puts its items in boxes so the result should suit your needs.

Possible major drawback: enumerate isn't nestable any more…

documentclass{article}
pagestyle{empty}
usepackage[margin=5mm,paperheight=5cm]{geometry}

usepackage{tasks}
RenewTasksEnvironment[label=arabic*.]{enumerate}[item]

begin{document}

begin{enumerate}
item
  Which bear is best?

  A. Honey bear
  B. Grizzly bear
  C. Black bear
  D. Teddy bear
  E. Polar bear
item
  How much snow per year is the preferable amount?

  A. 90 inches
  B. 120+ inches
  C. 30 inches
  D. 60 inches
end{enumerate}

end{document}

enter image description here

Answered by cgnieder on April 6, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP