15

I am trying to fit a text into a box within an overpic environment. kongo09 had a similar question fit-text-into-given-box-by-adjusting-the-fontsize, and a highly workable solution was provided by Werner:

\documentclass{article}
\usepackage{environ}

\newdimen\fontdim
\newdimen\upperfontdim
\newdimen\lowerfontdim
\newif\ifmoreiterations
\fontdim12pt

\makeatletter
\NewEnviron{fitbox}[2]{% \begin{fitbox}{<width>}{<height>} stuff \end{fitbox}
  \def\buildbox{%
    \setbox0\vbox{\hbox{\minipage{#1}%
      \fontsize{\fontdim}{1.2\fontdim}%
      \selectfont%
      \stuff%
    \endminipage}}%
    \dimen@\ht0
    \advance\dimen@\dp0
  }
  \def\stuff{\BODY}% Store environment body
  \buildbox
  % Compute upper and lower bounds
  \ifdim\dimen@>#2
    \loop
      \fontdim.5\fontdim % Reduce font size by half
      \buildbox
    \ifdim\dimen@>#2 \repeat
    \lowerfontdim\fontdim
    \upperfontdim2\fontdim
    \fontdim1.5\fontdim
  \else
    \loop
      \fontdim2\fontdim % Double font size
      \buildbox
    \ifdim\dimen@<#2 \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@>#2
      \moreiterationstrue
      \upperfontdim\fontdim
      \advance\fontdim\lowerfontdim
      \fontdim.5\fontdim
    \else
      \advance\dimen@-#2
      \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
  \box0% Typeset content
}
\makeatother

The defined fitbox environment can, of course, be used inside an overpic environment. Appending

\usepackage[percent]{overpic}
\usepackage{lipsum}

\begin{document}
\begin{overpic}[width=.3\textwidth,grid,tics=10]{picfile}
  \put(20,50) {% turns out not to be exactly the lower left
    \begin{fitbox}{5cm}{1cm}% unhandy units - desired box size depends on picture size
      \lipsum[1]%
    \end{fitbox}%
  }%
\end{overpic}
\end{document}

to the code above does basically what I want. However, neither is the (20,50) the bottom left corner, nor are 'cm', 'pts' etc. handy units.

Would it be possible to modify the above code such that instead of width and height the coordinates of the upper right corner can be passed and that the (20,50) is the lower left?

5
  • Did you try the picture package, that extends the picture environment so that coordinates can be expressed also with the unit of measure or with parameters such as .3\textwidth?
    – egreg
    Feb 16, 2012 at 21:08
  • No, I haven't. Will go through its documentation.
    – D.Roepo
    Feb 16, 2012 at 21:20
  • I couldn't solve it using the picture package. That was due to two reasons i) I didn't figure out how to run 'pdflatex -enable-write18' using TeXnicCentre and ii) I couldn't directly see how it should be used from its documentation
    – D.Roepo
    Feb 17, 2012 at 17:32
  • I don't think that picture requires -shell-escape. Actually a small test I made seems to work.
    – egreg
    Feb 17, 2012 at 17:34
  • You're right. After un-&reinstalling MikTeX and TeXnicCentre the picture package works fine. But it is still not obvious for me how to go from here...
    – D.Roepo
    Feb 19, 2012 at 10:17

2 Answers 2

13
+100

A minor defect of the macros you're using is that they put the text into a minipage, which makes somewhat difficult to predict where the reference point will end up.

So, first of all change the first lines in the definition of fitbox, avoiding the use of minipage that can mess up the vertical alignment (we need to use \hsize and not \textwidth and also set \linewidth, though):

\NewEnviron{fitbox}[2]{% \begin{fitbox}{<width>}{<height>} stuff \end{fitbox}
  \def\buildbox{%
    \setbox0\vbox{
      \hsize#1\textwidth=\hsize\linewidth=\hsize
      \parindent=0pt
      \fontsize{\fontdim}{1.2\fontdim}\selectfont
      \BODY\par}%
    \dimen@\ht0
    \advance\dimen@\dp0
  }
  \buildbox
  % Compute upper and lower bounds

In this way the reference point of the box so built will be on the lower left (at the baseline of the last line in it, to be precise).

Thus you have a way to place exactly the box; for instance

\documentclass{article}
\usepackage{graphicx}
\usepackage[percent]{overpic}
\usepackage{picture}

\begin{document}
\begin{overpic}[width=10cm,height=8cm,grid,tics=10]{filename}
  \put(2.5cm,3.5cm){%
    \begin{fitbox}{5cm}{1cm}%
Some text that will fit
Some text that will fit 
Some text that will fit
Some text that will fit 
    \end{fitbox}%
  }%
\end{overpic}
\end{document}

will place the text in the center of the picture.

Here's the result (using the draft option to graphicx so that the picture is invisible):


enter image description here

8
  • That works nicely, at least for the base line. Not so for the left boundary which is still slightly offset but not so important.
    – D.Roepo
    Feb 20, 2012 at 13:57
  • As for the relative sizing of the box, I thought about sth like this: \newcommand\wdt{.7} \begin{overpic}[width=\wdt\textwidth,grid,tics=10]{filename} \put(20,50){% \begin{fitbox}{.7\wdt\textwidth}{.1\heightOfResizedImg} How do I get LaTeX to make that multiple multiplication and how do I find '\heightOfResizedImg'. Or is this an unclever solution to begin with?
    – D.Roepo
    Feb 20, 2012 at 14:01
  • 1
    The height can be obtained by measuring the box: \sbox0{\includegraphics[...]{...}}\setlength{\heightOfResizedImage}{\ht0}. Unfortunately overpic uses scratch registers for this, so its measuring can't be accessed in a reliable way.
    – egreg
    Feb 20, 2012 at 14:11
  • This seems to be reliable enough for my purpose. I just came to realize that this whole macro does not work for enumitem environments... Can that be adjusted? Your own textinoval environment can handle that, but I couldn't merge these two macros (tex.stackexchange.com/questions/33417/…)
    – D.Roepo
    Feb 21, 2012 at 7:45
  • 2
    You should add \linewidth=\hsize in the definition. I'll modify the answer.
    – egreg
    Feb 21, 2012 at 8:57
13

I would use a slightly different approach, first try and get everything into a macro and enable usage of cm throughout. As a point of reference is best to use the bottom left hand corner of the box like a normal plot (easier to remember and to visualize).

Here is the MWE:

\documentclass{article}
\usepackage{graphicx,environ}
\usepackage{overpic}
\usepackage{picture}
\usepackage{xcolor}
\def\sampletext{Some text that will fit
 Some text that will fit 
 Some text that will fit
 Some text that will fit.}
\fboxsep=0pt
\fboxrule=0pt
\begin{document}
% #1 x position
% #2 y position
% #3 width
% #4 height
% #5 graphics
\long\def\combo(#1,#2)#3#4#5#6{%
   \begin{overpic}[width=#3, height=#4,  grid,tics=10]{#5}
   \parindent0pt
  \put(#1,#2){%
  \vbox{\leftskip5pt\rightskip5pt\hsize #3\color{white}\sffamily 
    #6\par}%
 }%
\end{overpic}}
\combo(0cm,1cm){10cm}{8cm}{./graphics/cardtrick}{\large \sampletext}
\medskip

\combo(0cm,1cm){10cm}{8cm}{./graphics/cardtrick}{\Large\bfseries \sampletext}
\end{document}

enter image description here

Ideally, this should be a key value macro. If I understood your requirements correctly please drop a comment and I will extend it to a key value answer. Please also note that I have added a \leftskip and a \rightskip to allow for a little bit of padding. You can zero it if you don't need it.

UPDATED VERSION

Version uses key values for exact position, padding borders and fitting text into a set of given parameters by re-sizing fonts.

enter image description here

Text can be aligned using the key values justified, raggedleft, raggedright and from the ragged2e package RaggedLeft and RaggedRight.

enter image description here

Typical entry with a few adjustments,

\combo[x=0pt,y=0pt,
            padding-left=5pt,
            padding-right=5pt,
            text-color=yellow,
            text-width=5cm,
            text-height=5cm,
            image-width=13cm,
            image-height=9.5cm,
            image-url=cardtrick,
            tics, showframe=true,
           ]{\lipsum[2]}  

Below is a MWE with the code, a bit longish but I am hoping to polish it a bit and create a proper |.dtx| file and package which I will post at github soon.

\documentclass{article}
\usepackage[latin]{babel}
\usepackage{graphicx,lipsum}
\usepackage{overpic}
\usepackage{picture}
\usepackage{xcolor}
\usepackage{ragged2e}
\usepackage{combo}
\RequirePackage{pgf}[2008/01/15]
\fboxsep=0pt
\fboxrule=1pt
\makeatletter
% We use two boxes, one to store the contents of the text
% and another to store and measure the image 
\newbox\textbox
% newif
\newif\if@ok
\@oktrue
\newif\ifshowframe
\newif\if@center
\newif\if@vcenter

% Step one define family
\pgfkeys{/combo/.is family}

% We store  keys mostly in their own macros 
\pgfkeys{/combo  
% text positioning, reference is 0,0 at the left hand
% corner of the image 
  x/.store in=\position@x,
  y/.store in=\position@y,
% text box padding
  padding-left/.store in=\padding@left,
  padding-left/.default=0pt,
  padding-right/.store in=\padding@right,
  padding-right/.default=0pt,
% 
  padding-bottom/.store in=\padding@bottom,
  padding-bottom/.default=0pt,
%
  padding-top/.store in=\padding@top,
  padding-top/.default = 0pt,
% text coloring
  text-color/.store in=\combo@textcolor,
  text-color/.default=white,
% text width
  text-width/.store in=\combo@textwidth,
  text-width/.default=100pt,
  text-height/.store in=\combo@textheight,
  text-height/.default=5cm,
  border-width/.store in=\combo@borderwidth,
  border-color/.store in=\combo@bordercolor,
  border-color/.default=red,
  image-width/.store in=\combo@imgwidth,
  image-width/.default=5cm,
  image-height/.store in=\combo@imgheight,
  image-height/.default=5cm,
  image-url/.store in=\combo@imageurl,
% the file name for the graphic
  image-url/.default=cardtrick,
  tics/.store in=\combo@tics,
  tics/.default=10,
  showgrid/.store in=\combo@grid,
  showgrid/.default ={,},
% define show frame key
  showframe/.is if= showframe,
  align/.is choice,
  align/center/.code={\@centertrue},
  align/vcenter/.code ={\@vcentertrue},
  text-align/.store in = \combo@textalign,
  border/.style={border-color=blue}
 }

% Process keys and set defaults, for later use
\def\setdefaults{\pgfkeys{/combo  
  x=12pt,y=50pt,
  padding-left,
  padding-right,
  padding-bottom,
  text-align=raggedleft,
  padding-top=0pt,
  showgrid=grid,
  }%
}

\setdefaults

% The approach we use is to have a set of predefined sizes
% to try out. If we cannot use any of these sizes, we fall back
% to scaling the fonts.
%
% We use a the list |\font@size@list| to hold all the allowable
% sizes for text. We also provide a command to add other sizes.
\newcommand{\font@size@list}{%
   \Huge,\huge,
   \LARGE,\Large
   \large,\normalsize,
   \small,\footnotesize,,
   \scriptsize,\tiny%
}

% Author command holding default font size
% nothing else needed
\newcommand\default@fontsize{\small}

% Getter and setter for fontsize
% set@text@size
\def\set@text@size#1{%
  \def\combo@text@size{#1}%
}


% we set default at \small
\set@text@size{\default@fontsize}

% holds the font details (Must add shape). Can be a bit automated
% as well


\newcommand\combo@inbox{}
% #2 x position of text block
% #3 y position
% #4 width
% #5 height
% #6 graphic
%
% We use pgf keys
\newcommand{\combo}[2][]{%
\setdefaults
\pgfkeys{/combo #1}

\def\combo@color{\color{\combo@textcolor}}
\def\combofont{%
  \bfseries\rmfamily
  \slshape\selectfont
  \combo@text@size\combo@color
}%

% We store the contents of the text block in a 
% box in order to measure the dimensions
\renewcommand{\combo@inbox}{%
    \sbox\textbox{\par \vbox{%
     \vskip\padding@top
    \leftskip\padding@left%
    \rightskip\padding@right%
    \hsize \combo@textwidth%
    \combofont%
    \expandafter\csname\combo@textalign\endcsname

     #2
     \vskip\padding@bottom
    \par
   }%
  }
}
\def\store@fontsize##1{%
 \def\selected@fontsize{##1}}

% We iterate through all the size to get
% find an acceptable size that can fit in the box

\@for\next:=\font@size@list\do{%
    \expandafter\set@text@size\next% 
    \next
% check and remeasure box
    \combo@inbox
   % \texttt{\expandafter\strip@prefix\meaning\next \the\ht\textbox}%
    \if@ok
        \ifdim\the\ht\textbox<\combo@textheight\relax
             \@okfalse
            %\fbox{\copy\textbox}\par%
            \expandafter\store@fontsize\next 
      \fi
   \fi
 }

% if not found we have reached here with no solution
% better log an error and retry, by scaling the box
\if@ok\store@fontsize{\tiny} THIS IS IN ERROR \else \fi
\@oktrue
% We use the overpic package to set a backgroundgrid and
% to place the origin of the text box.

\vspace{1.5\baselineskip}

\centering

\begin{overpic}[width=\combo@imgwidth, 
                       height=\combo@imgheight, 
                      grid=\combo@grid,
                      tics=\combo@tics]{\combo@imageurl}%
  \set@text@size\selected@fontsize
  \combo@inbox
  \put(\position@x,\the\dimexpr\position@y+\padding@bottom\relax){%
       {\ifshowframe
              \color{\combo@bordercolor}\fboxrule1pt\fbox{\copy\textbox}
         \else
              \color{red}\fboxrule0pt\fbox{\copy\textbox}
        \fi
       }%
  }
\end{overpic}

}

\begin{document}

\combo[x=5pt,y=80pt,
            padding-left=0pt,
            padding-right=0pt,
            padding-bottom=0pt,
            text-color,
            text-width=6cm,
            text-height=5cm,
            image-width=13cm,
            image-height=7.5cm,
            image-url=mandela,
            tics,showframe=true,
            border-color=yellow,
            text-align=raggedright,
            showframe=true,
            showgrid=true,
            border={border-color=blue}
            ]{Mandela has not made a public appearance since
               the 2010 World Cup final in Johannesburg.}  
\newpage
\combo[x=0pt,y=0pt,
            padding-left=5pt,
            padding-right=5pt,
            text-color=yellow,
            text-width=5cm,
            text-height=5cm,
            image-width=13cm,
            image-height=9.5cm,
            image-url=cardtrick,
            tics, showframe=true,
           ]{\lipsum[2]}  
\end{document}
7
  • I would like to always show the whole image and place a text box at given coordinates on it. In the definition of \combo provided here the text box's dimensions are identical to the picture's. I.e. reducing the height of the text box cops the image. The intention is that the box's dimensions are a (specifiable) fraction of the picture's.
    – D.Roepo
    Feb 20, 2012 at 14:20
  • @D.Roepo Thanks for the feedback. So if I understand you correctly, you want to input the text dimensions as say 0.3\imagewidth and 0.5\imageheight at position x,y relative? The width of the writing in my example, can be reduced by specifying a \leftskip \rightskip, try changing the values to see the effect, one can change these into inputs. Also I recommend you only constrain the width and not the height and manually adjust the size of the lettering in order to have text that matches the rest of the typography of the document.
    – yannisl
    Feb 20, 2012 at 14:40
  • Thanks, I didn't grasp the \rightskip's purpose. This pf course works. My main goal was the adjustment of the font size. Having an area on an image where to display text and try to have the font size consistent with the rest of the document. But in case it doesn't fit I would like to reduce it.
    – D.Roepo
    Feb 21, 2012 at 7:27
  • @D.Roepo thanks, now I understand better your requirements. I will update the answer this weekend, as I also need a good macro for annotating images.
    – yannisl
    Feb 21, 2012 at 7:39
  • @D.Roepo Please see updated answer.
    – yannisl
    Feb 26, 2012 at 8:48

You must log in to answer this question.

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