\_codedecl \lualineno {Line numbering <0.1, 2026-02-12>} \_directlua{require('lualineno')} \addto\_resetattrs{\lualineno{unset}} \_endcode \sec Loading `lualineno` To load the package you can use \begitems \style d * {\LaTeX} `\usepackage{lualineno}` * {\OpTeX} `\load[lualineno]` * {Plain} `\directlua{require('lualineno')}` \enditems \sec User Interface There is only one macro, `\lualineno`, which takes a single argument consisting of a list of `key=val` pairs which are separated by spaces (or end on lines). The possible keys are \begitems \style d * {define} Takes a list of `key=val` pairs in itself, and is used to define a new `lineno` type. see \ref[define]{line~@} for more details. * {defaults} Takes a list of `key=val` pairs in itself, and is used to set the default values of a newly defined `lineno` type. see \ref[defaults]{line~@} for more details. * {set} Takes a name of a defined `lineno`. Set this `lineno` as the active one. * {unset} Does not accept a value. Disables line numbers. * {label} Takes a list of tokens inside braces, e.g. `{label}`, these tokens will be fed into `\label` at a later stage. Should be used after a glyph (but it does not have to be right after it). * {anchor} Does not accept a value. When used after a vertical box, line numbers for lines inside that box will be positioned at the box boundaries. * {processbox} Takes an integer corresponding to a box. When using this key, numbers will be added to lines in this box. This is mainly useful for plain users who will need to use this in the output routine, but might be useful if someone wants to number lines in a strange order. \enditems Note that a line is numbered according to the attribute of the last node in it, so it is possible to change `lineno` type in the middle of the line. \secc Defining a Lineno When defining a lineno the following keys are available\lualineno{label={define}} \begitems \style d * {name} Accepts a string that will be used as the name for the `set` key. This key is mandatory. If the same name is used a second time, the parameters of the existing type will be modified. * {column} Takes an integer. Each `lineno` type can have different parameters in different columns, each definition only modifies the parameters for one column. The default is `1`. * {toks} Takes a list of tokens inside braces. These tokens will be executed each time before line numbers are added to a line. The tokens do not have to be fully expandable, but they should not create any nodes. The default is empty. * {left} Takes a list of tokens inside braces. These tokens will be fed into an `\hbox` which will be added to the left side of a line. The default is empty. * {right} Takes a list of tokens inside braces. These tokens will be fed into an `\hbox` which will be added to the right side of a line. The default is empty. * {line} Takes two boolean keys, `number` and `recurse`. If `number` is true then hlists of subtype `line` will be numbered, default is `true`. If `recurse` is true hlists of subtype `line` will be searched recursively to check if they have lines inside of them (for example minipage inside of a line), default is true. * {box} The same as `line` but for hlists of subtype `box`. * {alignment} The same as `line` but for hlists of subtype `alignment`. * {equation} The same as `line` but for hlists of subtype `equation`. * {displayalignment} The same as `line` but for hlists of subtype `alignment` which are created inside a display equation. * {offset} A boolean key. If set to false, offsets will be ignored, and line numbers will be added right before or after the lines. The default is `true`. \enditems If a key is not specified when defining a lineno, the default value will be used\lualineno{label={defaults}}. The default values can be changed using the `defaults` key, which accepts the same keys as `define` do, except for `name`. \secc Notes For Plain Users Plain users need to process the page box before it is shipped out. A simple example is \begtt \catcode`\@=11 \def\plainoutput{% \setbox0\vbox{\makeheadline\pagebody\makefootline}% \lualineno{processbox=0}% \shipout\box0 \advancepageno \ifnum\outputpenalty>-\@MM\else \dosupereject\fi } \catcode`\@=12 \endtt If `luatexbase` is not used, you will probably want to reallocate the attributes used by `lualineno`. You can do this with the keys `typeattr`, `colattr` and `markattr`, which each accept an integer (the attribute number). Note that in this case, all alignments will be treated as regular (even those inside display equations), and the `lualineno` callbacks will not be defined. \sec Examples \everytt{\typoscale[700/700]\hisyntax{tex}} \secc Number Lines Across the Document Since `pre_shipout_filter` is called inside the output routine, it is running inside a group, which means you should use global counters. \LaTeX/ already does that by default, in \OpTeX/ we need to use the `\global` prefix. \begEX \newcounter{lineno} \lualineno { define = { name = default toks = {\stepcounter{lineno}} left = {\tiny\thelineno \kern.8em} } set = default } \midEX \newcount\lineno \lualineno { define = { name = default toks = {\global\advance\lineno by 1} left = {\thefontsize[5] \the\lineno\kern.8em} } set = default } \endEX \secc Number Lines Per Page Since `pre_shipout_filter` is called inside the output routine, it is running inside a group, which means you should use local counters. \OpTeX/ already does that by default, in \LaTeX/ we need to reset the counter per page. \begEX \newcounter{lineno} \counterwithin{lineno}{page} \lualineno { define = { name = default toks = {\stepcounter{lineno}} left = {\tiny\thelineno \kern.8em} } set = default } \midEX \newcount\lineno \lualineno { define = { name = default toks = {\advance\lineno by 1} left = {\thefontsize[5] \the\lineno\kern.8em} } set = default } \endEX \secc Number Two Column Layout In this example, we add numbers to the right of lines in the first column and to the left in the second column. \begEX \newcounter{lineno} \lualineno { define = { name = default toks = {\stepcounter{lineno}} left = {\tiny\thelineno \kern.8em} } define = { name = default column = 2 toks = {\stepcounter{lineno}} right = {\kern.8em\tiny \thelineno} } set = default } \midEX \newcount\lineno \lualineno { define = { name = default toks = {\global\advance\lineno by 1} left = {\thefontsize[5]% \the\lineno\kern.8em} } define = { name = default column = 2 toks = {\global\advance\lineno by 1} right = {\kern.8em\thefontsize[5]% \the\lineno} } set = default } \endEX \secc Modulo If for example you want to print only every third line after the first, you can use something like \begEX \newcounter{lineno} \lualineno { define = { name = default toks = {\stepcounter{lineno}} left = {\ifnum\value{lineno}= \numexpr(\thelineno-1)/3*3+1\relax \tiny\thelineno\kern.8em\fi} } set = default } \midEX \newcount\lineno \lualineno { define = { name = default toks = {\global\advance\lineno by 1} left = {\ifnum\lineno= \numexpr(\lineno-1)/3*3+1\relax \thefontsize[5] \the\lineno\kern.8em\fi} } set = default } \endEX \secc Reference a Line You will need to create a label target, with `\refstepcounter` or `\wlabel`. Note that `\refstepcounter` cannot be inside the number box because the `\label` will be executed at an outer group. \begEX \newcounter{lineno} \lualineno { define = { name = default toks = {\refstepcounter{lineno}} left = {\tiny \thelineno \kern.8em} } set = default } \midEX \newcount\lineno \lualineno { define = { name = default toks = {\global\advance\lineno by 1} left = {\thefontsize[5]% \wlabel{\the\lineno}% \the\lineno\kern.8em} } } \endEX now `\lualineno{label={foo}}` will create a label named `foo` that can be referenced later. \secc Link to a Line When hyperref is loaded, `\refstepcounter` creates a node (a pdf destination), so we need to disable the node creation temporarily and use `\MakeLinkTarget` inside the box. There might be a better way though. In \OpTeX/ it is the same as with normal line reference, but we also put a destination node with `\dest`. \begEX \newcounter{lineno} \lualineno { define = { name = default toks = {\AssignSocketPlug {refstepcounter/target}{noop}% \refstepcounter{lineno}% \AssignSocketPlug {refstepcounter/target}{hyperref}} left = {\MakeLinkTarget{lineno}% \tiny\thelineno\kern.8em} } set = default } \midEX \newcount\lineno \lualineno { define = { name = default toks = {\global\advance\lineno by 1} left = {\thefontsize[5]% \wlabel{\the\lineno}% \dest[line:\the\lineno]% \the\lineno\kern.8em} } } \endEX \secc Ignore Line Numbers When Copying Text You can put the line number inside an empty ActualText span, although not all pdf readers support that. \begEX \newcounter{lineno} \lualineno { define = { name = default toks = {\stepcounter{lineno}} left = {\pdfextension literal page {/Span<>>BDC}% \tiny\thelineno \pdfextension literal page{EMC}% \kern.8em} } set = default } \midEX \newcount\lineno \lualineno { define = { name = default toks = {\global\advance\lineno by 1} left = {\thefontsize[5]% \pdfliteral page {/Span<>>BDC}% \the\lineno \pdfliteral page{EMC}\kern.8em} } set = default } \endEX \secc The `offset` and `anchor` Keys The following example demonstrates what they do \begtt \newcount\lineno \lualineno { defaults = { toks = {\advance\lineno by 1} left = {\the\lineno\kern.8em} right = {\kern.8em\the\lineno} } define = {name = offset} define = {name = nooffset offset = false} set = offset } \def\tmp{\noindent\hfil \frame{\vbox{ \hbox{Text}\hbox{Spanning}\hbox{Multiple}\hbox{Lines}\hbox to 5cm{} \vskip-\baselineskip}}} \tmp \medskip \tmp\lualineno{anchor} \medskip \lualineno{set=nooffset} \tmp \endtt \medskip \begingroup \newcount\linecount \lualineno { defaults = { toks = {\advance\linecount by 1} left = {\the\linecount\kern.8em} right = {\kern.8em\the\linecount} } define = {name = offset} define = {name = nooffset offset = false} set = offset } \def\tmp{\noindent\hfil \frame{\vbox{ \hbox{Text}\hbox{Spanning}\hbox{Multiple}\hbox{Lines}\hbox to 5cm{} \vskip-\baselineskip}}} \tmp \medskip \tmp\lualineno{anchor} \medskip \lualineno{set=nooffset} \tmp \endgroup \sec Callbacks There are three callbacks defined by `lualineno` \begitems \style O * `lualineno.pre_numbering`: A simple callback that is called before numbers are added to a line. This is where the tokens from the `toks` keyword are executed, and labels are created if \OpTeX/ is used. * `lualineno.numbering`: An exclusive callback that invokes the function that adds the numbers to lines. You can replace the function if you want to modify things. * `lualineno.post_numbering`: A simple callback that is called before numbers are added to a line. This is where labels are created if \LaTeX/ is used. \enditems None of the callbacks need a return value, and take as arguments the following (in order) \begitems \style O * `line`: The line node where numbers will be added. * `line_type`: A table of the parameters of the type of the `lineno` (see the implementation for details) * `offset`: The calculated horizontal offset of the line from the box margin. * `width`: The width of the page or column (or anchored box) containing the line. * `dir`: The direction of the vertical list containing the line. \enditems \_doc %%%%%%%%%%%%%%%%%%%%%%%%% example blocks \def\begEX{\_wipeepar\begingroup\_setverb \adef{ }{\_dsp}\adef\^^I{\t}% \def\t{\hskip \dimexpr\tabspaces em/2\relax}% \endlinechar=`^^J \startEX} \expanded{\def\noexpand\startEX#1\csstring\\midEX^^J#2\csstring\\endEX^^J{% \medskip \setbox0\vtop attr 4 = 1{\hsize=0.45\hsize \unexpanded{{\Blue\LaTeX} \begingroup\bracedparam\begtti{}#1}% \csstring\\endtt^^J}% \setbox1\vtop attr 4 = 2{\hsize=0.45\hsize \unexpanded{{\Green\OpTeX} \begingroup\bracedparam\begtti{}#2}% \csstring\\endtt^^J}% \noindent\hfil\noexpand\inoval{\vbox{\vskip10pt \line{\box0\hss\vtop to \dimexpr\dp1-\ht1{\Red\leaders\vrule width 1bp\vfil}\hss\box1}}}\medskip\endgroup}} %%%%%%%%%%%%%%%%%%%%%%%%% lists \_itemskipamount = 3.5pt plus 1pt \sdef{_item:d}{\aftergroup\dword} \def\dword#1#2{{\bf#2 }\ignorespaces} %%%%%%%%%%%%%%%%%%%%%%%%% \load[lualineno] \margins/1 a4 (1,1,1,1)in \newcount\lineno \lualineno { define = { name = default toks = {\global\advance\lineno by 1} left = {\_ewref\Xline{{\_the\lineno}}% \thefontsize[5]\wlabel{\the\lineno}% \_def\_destheight{20pt}\_dest[line:\_the\lineno]% \pdfliteral page{/Span<>>BDC}% \the\lineno\pdfliteral page{EMC}\kern2em} } define = { name = default column = 2 toks = {\global\advance\lineno by 1} right = {\_ewref\Xline{{\_the\lineno}}% \thefontsize[5]\kern2em\wlabel{\the\lineno}% \_def\_destheight{20pt}\_dest[line:\_the\lineno]% \pdfliteral page{/Span<>>BDC}% \the\lineno\pdfliteral page{EMC}} } define = { name = skip toks = {\global\advance\lineno by 1} left = {} } set = default } \load[doc] \colordef\EXbg{.2\Red} \ovalparams={\fcolor=\EXbg \roundness=6pt \shadow=N \lwidth=1bp \hhkern=-\roundness } \_openref \_immediate\_write\_reffile{% \def\noexpand\Xline\string#1{\def\noexpand\currline{{}{\string#1}}}% \_def\noexpand\_XtocA\string#1{\noexpand\_addto\noexpand\_toclist{{\string#1}}% \unexpanded{\_ea\_addto\_ea\_toclist\_ea{\currline}}}% } \suppresslongerror=1 \replmacro \_tocline {} {} {"pg:\#6", "line:\#7"} {} \insertoutline{Lua-Lineno} \tit Numbering lines in \LuaTeX/ \hfill Version: 0.1, 2026-02-12 \par \centerline{\it Udi Fogiel, 2026} The `lualineno` module provides flexible line numbering for \LuaTeX-based formats (Lua\LaTeX, \OpTeX, and Plain \LuaTeX). \insertoutline{Contents}\outlines 0 \notoc\nonum\sec Table of contents\hfill \typosize[8/]\bf line \maketoc \printdoctail lualineno.opm \sec Implementation \secc Lua module {\everytt={\typosize[8/10]\lualineno{set=skip}\_let\_printverbline=\_printcodeline \_hfuzz=1em \medskip} \commentchars-- \def\docfile{lualineno.lua} \ttline=-1 \_def\_printcomments{\_medskip {\_catcodetable0 \_typosize[10/12]\lualineno{set=default}\_everypar={}\_scantextokens\_ea{\_vcomments}\par}% \_bigskip \_ifx\_normalprintfilename\_undefined\_else \_let\_printfilename=\_normalprintfilename \_fi } \verbinput \hisyntax{lua} (3-) lualineno.lua } \secc \OpTeX/ package The \OpTeX/ package does not contain much. It is mainly here for the documentation, or in case someone prefers to type `\load[lualineno]` instead of `\directlua{require('lualineno')}`. \bgroup \lualineno{set=skip} \printdoc lualineno.opm % prints \_doc...\_cod parts + code before \_endcode \egroup \everytt{\typoscale[800/800]\lualineno{set=skip}} \secc \LaTeX/ package The \LaTeX/ package mostly contains patches to other packages to mark the column boxes. \verbinput \commentchars\%\% \vitt{lualineno.sty} \hisyntax{tex} (1-) lualineno.sty \bye \_cod \endinput