1

I would like to move the line numbers from the left side of the right column to its right side using lineno and multicol. Could you please help me how to reach this goal? My example is the following:

\documentclass{article}
\usepackage{lipsum}
\usepackage{lineno}
\usepackage{multicol}

\linenumbers

\title{test}

\begin{document}

\begin{multicols}{2}

\lipsum[1-1]

\end{multicols}

\end{document}
3
  • tex.stackexchange.com/questions/410596/…
    – js bibra
    Commented Feb 14, 2021 at 12:25
  • 1
    I think that this is an answer to another question. Multicol does not behave as twocolumn.
    – TobiR
    Commented Feb 14, 2021 at 14:37
  • basically you can't. At this point in time. multicol and lineno are incompatible. Commented Feb 14, 2021 at 14:40

3 Answers 3

1

Mutlicol is balancing its output and doing trial runs with different column breaks (at least at the end. For this it uses a different output routine. lineno is also installing its own output routine (by basically sending each line one by one to the OR to attach line numbers. The two do not know about each other and so conflict. To lineno, what multicol does looks like a single long column, so the attachment ends up on one side and the later cutting into columns just makes this wrong. But it would also a problem, because it is not clear what should be done if there are more than 2 columns produced by multicol.

Basically with both together, all you can do right now is to make the column separation large enough to allow the numbers there and have them always on the left or always on the right.

3
  • Is there a way to make multicols and lineno know each other?
    – Y. zeng
    Commented Jun 12, 2023 at 4:05
  • @Y.zeng in theory yes (TeX is Turing complete :-)) in practice no, not likely. It would require a lot of changes in both packages and I simply don't have the bandwidth to address that in the forceeable future. Commented Jun 12, 2023 at 16:16
  • Okay. It is a pity.
    – Y. zeng
    Commented Jun 13, 2023 at 2:31
0

enter image description here

\documentclass[twocolumn]{article}
\usepackage[switch]{lineno} % default option is 'left'
\usepackage{lipsum} % filler text

\begin{document}
    \linenumbers
    \lipsum[1-20]
\end{document}
3
  • Thanks, but I think that this is an answer to another question. Multicol does not behave like that twocolumn.
    – TobiR
    Commented Feb 14, 2021 at 14:37
  • this is not using multicol. It is a solution for twocolumn. Commented Feb 14, 2021 at 14:41
  • It is not balanced.
    – Y. zeng
    Commented Jun 12, 2023 at 13:59
0

With LuaTeX you can mark the columns with an attribute to determine which column it is at shipout time, there you can add the line numbers with the pre_shipout_filter callback.

In the following code, \lualinenosetup is a macro that defines different numbering types. It accepts five arguments

\lualinenosetup
    [ <column> ] 
    { <type> }
    { <preamble> }
    { <left box> }
    { <right box> }
  • The first is optional, if it is present this type will be typeset only in the column specified (0 means outside of columns, and it is the default).
  • The second is the type number. It adds the option to change types in the middle of a page.
  • The third is a code that is executed before the a line number box is packed.
  • The fourth is a code to be typeset in the left side of the line.
  • The fifth is a code to be typeset in the right side of the line.
\newattribute\lualinenotype
\lualinenotype=0

\directlua{
local is_defined = token.is_defined
local get_attribute = node.get_attribute
local put_next = token.put_next
local copy_list = node.copy_list
local insert_before = node.insert_before
local insert_after = node.insert_after
local type_attr = luatexbase.attributes['lualinenotype']
luatexbase.new_attribute('lualineno_col')
local col_attr = luatexbase.attributes['lualineno_col']
local function recurse(head, column)
    for n in node.traverse(head.list) do
        if n.id == 0 and (n.subtype == 1 or n.subtype == 6) then
            
            local type = get_attribute(n.head, type_attr)
            
            tex.runtoks(function()
                if is_defined('__lualineno_' .. type .. '_' .. column .. ':',true) then
                    put_next(token.create('__lualineno_' .. type .. '_' .. column .. ':'))
                elseif is_defined('__lualineno_' .. type .. '_' .. 0 .. ':',true) then
                    put_next(token.create('__lualineno_' .. type .. '_' .. 0 .. ':'))
                else
                    put_next(token.create('__lualineno_' .. 0 .. '_' .. 0 .. ':'))
                end
            end)
            
            local left_box = copy_list(tex.getbox('l__lualineno_left_box'))
            local right_box = copy_list(tex.getbox('l__lualineno_right_box'))
            
            local left_kern = node.new("kern")
            left_kern.kern = -left_box.width - n.shift
            
            local shift_kern = node.new("kern")
            shift_kern.kern = n.shift
            
            n.head = insert_before(n.list,n.head,shift_kern)
            n.head = insert_before(n.list,n.head,left_box)
            n.head = insert_before(n.list,n.head,left_kern)
            
            if n.subtype == 6 then
                n.head = insert_after(n.list,n.tail,node.copy(shift_kern))
            end
            n.head = insert_after(n.list,n.tail,right_box)

        elseif n.list then
            if get_attribute(n, col_attr) then
                column = get_attribute(n, col_attr)
            end
            recurse(n, column)
        end
    end
end
luatexbase.add_to_callback("pre_shipout_filter", function(head)
    recurse(head,0)
    return true
end, "lineno")
}

\ExplSyntaxOn

\box_new:N \l__lualineno_left_box
\box_new:N \l__lualineno_right_box

\NewDocumentCommand \lualinenosetup { O{0} m m m m }
  {
    \cs_set:cpn { __lualineno_#2_#1: }
      {
        #3 
        \hbox_set:Nn \l__lualineno_left_box { #4 }
        \hbox_set:Nn \l__lualineno_right_box { #5 }
      } 
  }

\ExplSyntaxOff

\documentclass{article}

\usepackage{multicol}
\makeatletter
\def\lualineno@setcol@attr{\directlua{
    local right_box = token.create('mult@rightbox').index
    local i = right_box
    local last = tex.count['doublecol@number'] - 2
    local column = 0
    while i < last do
        i = i + 2
        column = column + 1
        tex.box[i][luatexbase.attributes['lualineno_col']] = column
    end
    tex.box[right_box][luatexbase.attributes['lualineno_col']] = column + 1
}}
  
\edef\lualineno@dbslash{\csstring\\\csstring\\} 
\directlua{
  token.set_macro("page@sofar", token.get_macro("page@sofar"):gsub("\lualineno@dbslash mc@align@columns", 
      "\lualineno@dbslash lualineno@setcol@attr \lualineno@dbslash mc@align@columns"))
}
\makeatother
\usepackage{lipsum}
\usepackage{xcolor}
\usepackage{luacolor}
\newcounter{lineno}
\lualinenosetup{0} % fallback
    {\stepcounter{lineno}}
    {\thelineno\kern0.8em}
    {}
\lualinenosetup[2]{0} % in the second column print only in the right side
    {\stepcounter{lineno}}
    {}
    {\kern0.8em\thelineno}
\lualinenosetup[1]{1}
    {}
    {\color{red}*\kern0.8em}
    {}
\lualinenosetup[2]{1}
    {}
    {\color{pink}*\kern0.8em}
    {\color{yellow}\kern0.8em *}
\lualinenosetup[3]{1}
    {}
    {}
    {\color{green}\kern0.8em *}


\begin{document}

\begin{multicols}{2}
\lipsum[1]
\[
1+1=2
\]
\lipsum[2]
\[
1+1=2
\]
\lipsum[1]
\begin{itemize}
\item \lipsum[1][1]
\item \lipsum[2][1]
\item \lipsum[3][1]
\end{itemize}
\end{multicols}
\newpage
\setlength\columnsep{25pt}
\begin{multicols}{3}
\lipsum[1][1-3]
\lualinenotype=1
\[
1+1=2
\]
\lipsum[2]
\[
1+1=2
\]
\lipsum[1]
\begin{itemize}
\item \lipsum[1][1]
\item \lipsum[2][1]
\item \lipsum[3][1]
\end{itemize}
\end{multicols}


\end{document}

page 1 page 2

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .