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.

Is there a way to make text size and alignment automatically "best fit" on a page? So that a large amount of text will be constrained on the page in a small font, but a small amount of text can be of larger font and centered properly?

The best way I found to fit lots of text on a page is with:

\begin{minipage}{width}
...
\end{minipage}
\newpage

...but when there is a smaller amount of text, the font remains small and is awkwardly aligned at the top of the page, whereas I'd like it to be centered vertically and horizontally. I tried \vfill with \minipage, but it makes no difference.

For smaller amounts of text, I succeeded with:

\mbox{}
\vfill
\begin{center}
....
\end{center}
\vfill
\newpage

This centered the text perfectly, but I couldn't constrain a large block to one page with this. I tried combining minipage and mbox, but minipage overrode the vfill spacing and the alignment was screwed up again for small amounts of text.

share|improve this question
    
What is the point of the minipage in your first listing? –  TH. Aug 24 '10 at 4:54

2 Answers 2

up vote 10 down vote accepted

Okay, TeX isn't really designed with this in mind. That said, it can be done (sort of).

\documentclass{article}
\usepackage{lmodern}
\newdimen\fontdim
\newdimen\upperfontdim
\newdimen\lowerfontdim
\newif\ifmoreiterations
\fontdim12pt
\makeatletter
\def\buildbox{%
        \setbox0\vbox{\fontsize{\fontdim}{1.2\fontdim}%
                      \selectfont
                      \centering
                      \stuff}%
        \dimen@\ht0
        \advance\dimen@\dp0
        %\message{Total height: \the\dimen@^^J}
}

\newcommand\fillthepage[1]{%
        \protected@edef\stuff{#1}%
        \buildbox
        % Compute upper and lower bounds
        \ifdim\dimen@>\textheight
                \loop
                        \fontdim.5\fontdim
                        \buildbox
                \ifdim\dimen@>\textheight
                \repeat
                \lowerfontdim\fontdim
                \upperfontdim2\fontdim
                \fontdim1.5\fontdim
        \else
                \loop
                        \fontdim2\fontdim
                        \buildbox
                \ifdim\dimen@<\textheight
                \repeat
                \upperfontdim\fontdim
                \lowerfontdim.5\fontdim
                \fontdim.75\fontdim
        \fi
        % Now try to find the optimum size
        \loop
                %\message{Bounds: \the\lowerfontdim\space
                %         \the\fontdim\space \the\upperfontdim^^J}
                \buildbox
                \ifdim\dimen@>\textheight
                        \moreiterationstrue
                        \upperfontdim\fontdim
                        \advance\fontdim\lowerfontdim
                        \fontdim.5\fontdim
                \else
                        \advance\dimen@-\textheight
                        \ifdim\dimen@<10pt
                                \lowerfontdim\fontdim
                                \advance\fontdim\upperfontdim
                                \fontdim.5\fontdim
                                \dimen@\upperfontdim
                                \advance\dimen@-\lowerfontdim
                                \ifdim\dimen@<.2pt
                                        \moreiterationsfalse
                                \else
                                        \moreiterationstrue
                                \fi
                        \else
                                \moreiterationsfalse
                        \fi
                \fi
        \ifmoreiterations
        \repeat
        % build the page
        \newpage
        \null
        \vfill
        \box0
        \vfill
}
\makeatother
\usepackage{lipsum}
\begin{document}
\fillthepage{\lipsum}
\fillthepage{\lipsum[1]}
\fillthepage{Very little}
\end{document}

You can uncomment out the \messages if you want to see how it decides on the font sizes to use. It doesn't make any effort to constrain horizontal size, so if you don't give it enough text to work with, you'll run off the right side of the page. (Try \fillthepage{X} for an example of that.)

It's pretty much essential that you use a vector font, otherwise it'll tend to go back and forth between two font sizes, spewing warnings and probably never converge. That's why I have the \usepackage{lmodern}.

I'm sure there are (many) cases where this doesn't work, but it was sort of fun to write.

Edit: Maybe that \protected@edef should just be a \def. I was originally thinking expanding it would be better, but now I'm not so sure

share|improve this answer
1  
+1, TH. It doesn't happen every day but today sure was one of them. Congrats, your answer left me smiling from ear to ear. :)) –  Geoffrey Jones Aug 24 '10 at 7:25
    
Thanks. =) The best part is, someone who's actually good at TeX will point out the trivial solution or package that someone has already written to handle this sort of thing. –  TH. Aug 24 '10 at 7:30
    
Thanks so much, TH! Your code worked perfectly. I'm very new to tex and there's no way I would have been able to piece something like that together myself. :) –  hypatia Aug 24 '10 at 17:33
    
This is very cool! One clarification before I open a separate question: How would I constrain it to a box of given dimensions? For the height, I can surely replace \textheight with a parameter, but how do I constrain the width? –  kongo09 Aug 17 '11 at 16:24
    
@kongo09, If I were to rewrite this, I'd I'd replace the \null \vfill\box0\vfill with \vbox to\textheight{\vfill\box0 \vfill}, as well as replace the \protected@edef with just a \def and all the 0s with \z@ for that matter. And of course, you'd replace all \textheight (including the one I just added in this comment) with a parameter. You can give \buildbox a parameter that is the width and replace its box with \setbox0\vbox{\hsize=#1 \fontsize...}`. –  TH. Aug 19 '11 at 4:13

I'm not quite sure what you are actually trying to do. But "but minipage overrode the vfill spacing" sounds as if you have put the \vfill inside the minipage. This makes only sense if you give the minipage a specific height (which is possible with an optional argument). If you want to move the minipage itself the \vfill must go outside.

You can use varwidth to get a minipage which adjust its width to its content. You can use \resizebox to scale a minipage to a given width.

Btw: Center things horizontally is in general a well-defined conzept: Chars, boxes and words have only one width and the reference point one wants to use is clear. This is different when you want to center things vertically. Chars and boxes have height and depth. Do you want to center a "A" along the baseline or along a line somewhere in the middle of the char? You can see the problem if you remove the [b] in the last example.

\documentclass{article}
\usepackage{varwidth,lipsum,graphics}
\begin{document}
\vspace*{\fill}

{\centering
 \begin{minipage}{0.5\textwidth}
  \lipsum[1]
 \end{minipage}
\par}


\vfill

\newpage
\vspace*{\fill}

{\centering
 \begin{varwidth}{0.5\textwidth}
  abc
 \end{varwidth}
\par}


\vfill
\newpage
\vspace*{\fill}

{\centering
 \begin{minipage}{0.5\textwidth}
  \centering abc\\cdeaddff
 \end{minipage}
\par}


\vfill

\newpage
\vspace*{\fill}

{\centering
 \resizebox{\textwidth}{!}{%
 .\begin{varwidth}[b]{0.5\textwidth}
  abc
 \end{varwidth}}
\par}
\vfill
\end{document}
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.