3

Is is possible to get first box in current vertical list? If it is impossible, is it possible to define a macro that gets an argument and return first line (first box of vertical list) of that argument. Excuse me for my bad English. Thanks

Edit

For calrify my quesion look at this wme and ouput of it.

\documentclass{article}
\usepackage{lipsum}
\def\gettfirstline#1{I want this macro return\par Nam dui ligula, fringilla a, euismod sodales, sollicitudin vel, wisi. Morbi auctor
}
\setlength\parindent{0pt}
\begin{document}
\lipsum[2]
\vspace{1.5cm}
\gettfirstline{\lipsum[2]}
\end{document}

Output: enter image description here

  • My answer is not using tex-core... but two packages instead... Feel fre to ask me delete it... Also I am not sure if the term 'first box' is the 'first item' as in my answer... If so, please edit your post to correct 'box' to 'item' – koleygr Jul 05 '18 at 11:47
  • @koleygr Thanks for your attention. I edit my question – mojtaba baghban Jul 05 '18 at 12:15
  • maybe this could help: https://tex.stackexchange.com/q/4135/4918 – Tobi Jul 05 '18 at 12:24

2 Answers2

4

You can assign your data to a \vbox and then extract the first box with \vsplit. It is quite hacky and probably fails if your box does not start with text and it only gives you the box. So in contrast to the solutions in Changing the style of the first *typeset* line of a paragraph you can't change the formatting anymore, but the spacing is preserved:

\documentclass{article}
\usepackage{lipsum}
\newbox\mylocalbox
\newbox\myglobalbox
\def\Ggetfirstline{%
  \vfuzz\maxdimen
  \setbox\mylocalbox=\vsplit0to1sp
  \setbox\mylocalbox\vbox{%
    \unvbox\mylocalbox
    \global\setbox\myglobalbox\lastbox
  }%
  \endgroup
  \box\myglobalbox
}
\def\Getfirstline{\aftergroup\Ggetfirstline}
\def\getfirstline{\begingroup\afterassignment\Getfirstline\setbox0\vbox}
\setlength\parindent{0pt}
\begin{document}
\lipsum[2]
\vspace{1.5cm}
\getfirstline{\lipsum[2]}
\end{document}

This code inserts the first line as the same type of box as it originally was, normally a \hbox. You can also keed the box wrapped in a vbox to keep horizontal displacement in place:

\documentclass{article}
\usepackage{lipsum}
\newbox\mylocalbox
\newbox\myglobalbox
\def\Ggetfirstline{%
  \vfuzz\maxdimen
  \setbox\mylocalbox=\vsplit0to1sp
  \global\setbox\myglobalbox\vbox{\unvbox\mylocalbox}
  \endgroup
  \box\myglobalbox
}
\def\Getfirstline{\aftergroup\Ggetfirstline}
\def\getfirstline{\begingroup\afterassignment\Getfirstline\setbox0\vbox}
\setlength\parindent{0pt}
\begin{document}
\lipsum[2]
\vspace{1.5cm}
\getfirstline{\lipsum[2]}
\end{document}

enter image description here

If you are open to use LuaTeX, you can use Lua to create a more general and robust macro which always gives you the first box in the current vertical list, even if you are in the main vertical list: (This is in plain LaTeX, just add \documentclass, \begin{document}, \end{document} if you prefer LuaLaTeX)

\def\setboxtovhead{\directlua{
  local vlistid, hlistid = node.id'vlist', node.id'hlist';
  local vlistlevel = tex.nest.ptr;
  while math.abs(tex.nest[vlistlevel].mode) \csstring~= 1 do
    vlistlevel = vlistlevel - 1
  end
  local nest = tex.nest[vlistlevel]
  local head = nest.mode == 1 and tex.lists.page_head or nest.head.next;
  while head and head.id \csstring~= vlistid and head.id \csstring~= hlistid do
    head = head.next
  end
  tex.box[token.scan_int()] = node.copy(head)
}}
\parindent0pt\parskip1em
abc\hfil\break def\hfil\break ghi

\setboxtovhead0
The first line was:\par
\box0
\end

enter image description here

  • Thanks. unfortunatele I have to use xetex. and the first solution make error. I use xelatex – mojtaba baghban Jul 05 '18 at 13:52
  • @mojtababaghban The first version is fixed now and works with XeLaTeX. I accidentally inserted a debugging version instead of the working code before. – Marcel Krüger Jul 05 '18 at 14:41
  • Thanks. This work good just whet argument start with display math mode firstline is not centered. – mojtaba baghban Jul 05 '18 at 18:12
  • @mojtababaghban That is caused by unwrapping the hbox. I added another version omitting this step. – Marcel Krüger Jul 06 '18 at 08:51
  • In this wme
    \documentclass{article}
    \usepackage{lipsum}
    \newbox\mylocalbox
    \newbox\myglobalbox
    \def\Ggetfirstline{
      \vfuzz\maxdimen
      \setbox\mylocalbox=\vsplit0to1sp
      \global\setbox\myglobalbox\vbox{\unvbox\mylocalbox}
      \endgroup
      \box\myglobalbox
    }
    \def\Getfirstline{\aftergroup\Ggetfirstline}
    \def\getfirstline{\begingroup\afterassignment\Getfirstline\setbox0\vbox}
    \setlength\parindent{0pt}
    \begin{document}
     Hi \[a^2+b^2=c^2\] \lipsum[2]
    \vspace{1.5cm}
    \getfirstline{Hi \[a^2+b^2=c^2\] \lipsum[2]}
    \end{document}
    

    getfirtline return two lines.

    – mojtaba baghban Jul 06 '18 at 10:24
3

A trick can be to set infinite \clubpenalty, so a \vsplit operation will catch that penalty and the box will be split there:

\documentclass{article}
\usepackage{lipsum}

\newsavebox\mainbox
\newsavebox\firstbox

\newcommand{\getfirstline}[1]{%
  \begingroup
  \setbox\mainbox=\vbox{%
    \clubpenalty=-10000
    \predisplaypenalty=-10000
    #1%
  }%
  \vbadness=10000 % don't be bothered with underfull boxes
  \setbox\firstbox=\vsplit\mainbox to \maxdimen
  \setbox\firstbox=\vbox{\unvbox\firstbox}
  \box\firstbox
  \endgroup
}

\begin{document}

\lipsum[2]
\getfirstline{\lipsum[2]}
\lipsum[2]

\end{document}

enter image description here

egreg
  • 1,121,712
  • this wme not work ```\documentclass{article} \usepackage{lipsum} \usepackage{mathtools} \newbox\mainbox \newbox\firstbox

    \newcommand{\getfirstline}[1]{% \begingroup \setbox\mainbox=\vbox{\clubpenalty=-10000 #1}% \vbadness=10000 % don't be bothered with underfull boxes \setbox\firstbox=\vsplit\mainbox to \maxdimen \setbox\firstbox=\vbox{\unvbox\firstbox} \box\firstbox \endgroup } \begin{document}

    Hi How are you? [a^2+b^2=c^2] \lipsum[2] \vspace{1cm} \getfirstline{Hi How are you? [a^2+b^2=c^2] \lipsum[2]}

    \end{document}```

    – mojtaba baghban Jul 05 '18 at 21:06
  • @mojtababaghban Sorry? What's not working? In what situation? – egreg Jul 05 '18 at 21:07
  • in this situation \documentclass{article} \usepackage{lipsum} \usepackage{mathtools} \newbox\mainbox \newbox\firstbox \newcommand{\getfirstline}[1]{ \begingroup \setbox\mainbox=\vbox{\clubpenalty=-10000 #1} \vbadness=10000 \setbox\firstbox=\vsplit\mainbox to \maxdimen \setbox\firstbox=\vbox{\unvbox\firstbox} \box\firstbox \endgroup } \begin{document} Hi How are you? \[a^2+b^2=c^2\] \lipsum[2] \vspace{1cm} \getfirstline{Hi How are you? \[a^2+b^2=c^2\] \lipsum[2]} \end{document} getfirstline macro return 3 lines instead of first line – mojtaba baghban Jul 06 '18 at 07:39
  • @mojtababaghban Setting \predisplaypenalty is needed, fixed. – egreg Jul 06 '18 at 08:31
  • Now previous problem is fixed but when getfirstline argument start with display math mode, getfirstline return nothing. – mojtaba baghban Jul 06 '18 at 09:52
  • @mojtababaghban No box should start with display math mode. – egreg Jul 06 '18 at 10:00
  • Is there a command that check argument start with display math mode? – mojtaba baghban Jul 06 '18 at 10:28