Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I'd like to place a horizontal rule above each section title, to separate the new section from the text before it. But I don't want the rule to appear if the section begins on a new page, since that would place the rule at either the bottom of one page or the top of the next, both of which would look bad.

I think the first step is to put a \nobreak after the \hrule, so that it'll never appear at the very bottom of a page, but it can still appear at the top of one. To prevent that, what I'd like to do is emit the rule conditionally, only if there's other material on the current page already, but because of the way TeX's page builder works, the page break might not be chosen until after the rule has already been added to the main vertical list. In other words, the condition isn't known at the time when the decision is made. So I don't think that'll work.

I know that an output routine has access to the material that didn't fit on the page, so it can put it back onto the "recent contributions" list. Can it remove things from the beginning of that material? If so, how could it determine whether the new page begins with this rule it should remove?

Alternatively, when TeX discards glue and penalties that follow a page break, can I make it discard a rule box too?

share|improve this question

2 Answers 2

up vote 7 down vote accepted

This very topic came up some months ago in the comp.text.tex discussion group; see draw horizontal rule IF not at top of a new page.

Building on the answer to that question, here's a MWE that achieves your goal. Note that I define a new command called \emphsection which first checks if it's close to the bottom of the page; if it's not, it draws a rule (except if it's already at the top of the page) and then invokes the ordinary \section command.

\documentclass[letterpaper,10pt]{article}
\usepackage{lipsum,needspace}

\newcommand{\emphrule}{%
  \par\vspace{2\bigskipamount}
  \leaders\vrule width \textwidth\vskip 0.4pt} 

\newcommand{\emphsection}[1]{\needspace{4\baselineskip}
    \emphrule\nobreak
    \section{#1}}

\begin{document}
\emphsection{First}
\lipsum[1]

\emphsection{Second}
\lipsum[2]

\emphsection{Third}
\lipsum[3]

\emphsection{Fourth}
\lipsum[4]

\emphsection{Fifth}
\lipsum[2]

\end{document}

If you compile this latex program, you'll see that sections 1 and 4, which start at the top of a page, do not have a horizontal rule drawn above the section headers.

I create a new command, \emphsection, rather than redefining the existing \section command because you may want to have the freedom to go back to the old \section command at some point. If you're sure you will not want to use the old sectioning command, you could first do a \let\origsection\section and then issue the command \renewcommand{\section}[1]{... \origsection{#1}.

Obviously, you will want to play with the amount of vertical separation between the end of a section and the horizontal rule; the instruction \vspace{2\bigskipamount} in the MWE is just a suggestion. Similarly, the command \needspace{4\baselineskip} will likely have to be adjusted if you use something like double-spaced lines and/or unusual font sizes in the section headers. Happy TeXing!

share|improve this answer
    
Actually, this works even without the \needspace if I redefine \@secpenalty to be 10000 to really prevent a break between the rule and the heading. The real insight here, for me, is the use of \leaders to produce the rule: that makes TeX treat it as glue, so it's automatically discarded when it follows a page break. –  Wyzard Oct 21 '11 at 7:21

The needspace package provides an easy means to accomplish this. It defines the command \needspace{<length>} that checks to see whether <length> is still available on the remainder of the page. If not, then a \break is issued, otherwise nothing is done. I've taken the exact code from the command \needspace, and adapted it to actually condition on whether or not there is enough space:

\usepackage{xparse}% http://ctan.org/pkg/xparse
...
\NewDocumentCommand{\ifneedspace}{m O{\relax}}{%
  \par \penalty-100\begingroup
  \setlength{\dimen@}{#1}%
  \dimen@ii\pagegoal \advance\dimen@ii-\pagetotal
  \ifdim \dimen@>\dimen@ii
    \break
  \else
    #2
  \fi\endgroup%
}

This new \ifneedspace{<length>}[<success>] command also checks to see whether <length> is available on the page. If this is the case, then <success> is executed. This condition is optional (hence the square brackets [ ]) and defaults to \relax (which does nothing). If there actually is not enough space on the page, it only executes \break. \ifneedspace is defined using the interface provided by xparse.

To make things a little more fancy, I've added the \myrule definition from How do I insert a border below text? as well. This requires the xcolor package for a selection of colours.

You can play around with the required <length>, but it seems like 6\baselineskip works. This allows for enough space of the rule (at 2pt thickness and 6pt total gap plus the section heading). Different lengths will, of course, require a different <length> to be used.

Here is the entire minimal working example illustrating the above concepts:

\documentclass{article}
\usepackage{needspace}% http://ctan.org/pkg/needspace
\usepackage{xparse}% http://ctan.org/pkg/xparse
\usepackage{xcolor}% http://ctan.org/pkg/xcolor
\newcommand{\Text}{% Dummy text
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas 
vitae libero lectus. Sed justo nunc, bibendum at euismod vel, molestie 
vestibulum ipsum. Aenean tincidunt vestibulum nulla, ut feugiat mi 
malesuada quis. Vestibulum eu tincidunt dui. Aliquam erat volutpat. 
Nam mauris nibh, placerat in blandit sed, scelerisque quis justo. 
Duis ac libero nec leo ullamcorper pellentesque sed at libero.\par}

\makeatletter
\NewDocumentCommand{\ifneedspace}{m O{\relax}}{%
  \par \penalty-100\begingroup
  \setlength{\dimen@}{#1}%
  \dimen@ii\pagegoal \advance\dimen@ii-\pagetotal
  \ifdim \dimen@>\dimen@ii
    \break
  \else
    #2
  \fi\endgroup%
}
\NewDocumentCommand{\myrule}{O{1pt} O{3pt} O{black}}{%
  \par\nobreak % don't break a page here
  \kern\the\prevdepth % don't take into account the depth of the preceding line
  \kern#2 % space before the rule
  {\color{#3}\hrule height #1 width\hsize} % the rule
  \kern#2 % space after the rule
  \nointerlineskip % no additional space after the rule
}
\makeatother
\begin{document}

\section{First section}
\Text \Text \Text \Text \Text \Text

text \par text % \par text

\ifneedspace{6\baselineskip}[\myrule[2pt][3pt][orange]]
\section{Second section}
\Text \Text \Text \Text \Text \Text \Text

\end{document}

enter image description here

If you uncomment the additional \par text, you obtain the following output:

enter image description here

It would be possible to incorporate the \ifneedspace command within the standard \section command using letltxmacro. Or, define your own \Section command that incorporates the above.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.