[Tex/LaTex] graphics – dynamic image placement

graphicsmulticolpicinspositioningspacing

This is a follow up to my post here: Graphics – Images off the page in multicolumn environment

Using C#, XML, SQL, and LaTeX, I've managed to create a system which retrieves database information and fills LaTeX commands to automatically create nicely typeset catalogs to send to customers. The beauty of this system is that it is completely hands off, no LaTeX code modification is necessary after all of the commands are written.

The catalog uses the multicol environment since each section within the catalog may have a different number of columns. I found that text-wrapping images in the multicol environment works best when using the picins package. My previous post uses the needspace package to automatically break a column to ensure that the image does not go off the page. The current trouble is that the needspace package leaves the catalog with undesirable whitespace which not only diminishes some esthetic appeal, but also accumulates and adds extra pages to the catalog.

The following code:

\documentclass{book}
\usepackage{multicol}
\setlength{\columnsep}{0.375 in}
\usepackage{picins}
\usepackage{graphicx}
\usepackage[usenames,dvipsnames]{color}
\usepackage[includeheadfoot, top=.25in, bottom=.25in, left=.5in, right=.5in]{geometry}
\usepackage{needspace}    
\usepackage[english]{babel}
\usepackage{blindtext}

\newcommand{\ItemImage}[5]%
{\pichskip{#5 in}\parpic[#2][#3]{\includegraphics[width=#4 in]{#1.png}}}

\newcommand{\ItemDescription}[1]%
{\noindent\color{black}#1\newline}

\begin{document}
\begin{multicols*}{2}
Book Title Number One\\
This is the subtitle of the first book\\
First Name, Last Name\\
\ItemImage{book1}{sr}{r}{1.25}{.125}
\ItemDescription{\blindtext[3] I was often using any of the available “lorem ipsum” generators on the web while testing different things in Latex until I discovered that the Latex distribution provides packages generating blind text, which is definitely more convenient. With just a few lines of code}
000-0-00000-000-0,Hb,\$19.95\\
Book Series\\
6 x 9,100,2012\\
\EndRule

\needspace{20\baselineskip}
Book Title Number Two\\
This is the subtitle of the second book\\
First Name,Last Name\\
\ItemImage{book1}{sr}{r}{1.25}{.125}
\ItemDescription{\blindtext}
000-0-00000-000-0,Hb,\$19.95\\
Book Series\\
10 x 12,500,2012\\
\EndRule

\end{multicols*}
\end{document}

creates something similar to:

The ideal situation would be (this graphic wasn't made from LaTeX):

So in the ideal situation, rather than the image being placed before the book description:

*Put Image Here* This would be the description starting in column 1. This line is in
column 1 also.  This line can't fit in column 1 and has moved to column 2

The image would be placed within the book's description:

This would be the description starting in column 1. This line is in column 1.  *Put 
Image Here* This line can't fit in column 1 and has moved to column 2

Since the text is automatically placed from the database, there would need to be some way for LaTeX to automatically figure out where to correctly place the image. Floating is what comes to mind, however text wrapping in a multicol environment is essential in which picins does the best job and doesn't float.

My ultimate question is:

Is there a way to calculate the lines remaining in a column, compare that value to the lines an image takes up, and place the image dynamically within a block of text at the point where the text breaks to the next column?

A rather round about way to asking a question but I'm very open to suggestions and points in the right direction to how to achieve the ideal situation shown above.

Best Answer

Here is a not too perfect concept. It uses pgfpagesnodes to determine whether there is enough room to place the picture. Special thanks to Garbage Collector for making me aware of this question. An unsolved problem is just too tempting.

  • The description next needs to be divided. TikZ's foreach uses , as a separator, which isn't ideal. So I asked a question how to change it, and thanks to egreg, it now uses | (but nearly any character is possible).
  • The picture will be set as early as possible. If it can't be set in the text, it will be placed alone at the end. If that also fails, an error message is displayed in red.
  • For some reason, both picins and wrapfig fail when I tried just putting text after them. As picins needs to be ended by \picskip{0}, the subdivisions of the descriptions should not be to short. To avaoid pushing the picture to the very end, they should not be to long. Five sentences of the length of my sample sentence should be okay (with square images of 1.25 inch).
  • Probably many compilations are neccessary. As TikZ's remember picture option needs two compiles, things might change on the second compile. This may push a later image over the edge, which then nneds a recompile and so on and so on.
  • The red dots are just there to show whre putting the image will be tried.
  • For some reason unknown to me, there are suspicious spaces on all non-image description paragraphs.
  • There are most likely other issues, input for improvement is more than welcome.

Code

\documentclass{book}
\usepackage{multicol}
\setlength{\columnsep}{0.375 in}
\usepackage{picins}
\usepackage{graphicx}
\usepackage[usenames,dvipsnames]{color}
\usepackage[includeheadfoot, top=.25in, bottom=.25in, left=.5in, right=.5in]{geometry}
\usepackage[english]{babel}
\usepackage{tikzpagenodes}
\usepackage{xifthen}
\usepackage{xparse}
\ExplSyntaxOn

\NewDocumentCommand{\Foreach}{ m O{,} m}
 {
  \__foreach_main:nn { #1 } { #2 } { #3 }
 }

\seq_new:N \l__foreach_arg_seq
\clist_new:N \l__foreach_braced_clist

\cs_new_protected:Npn \__foreach_main:nn #1 #2 #3
 {
  \seq_set_split:Nnn \l__foreach_arg_seq { #2 } { #3 }
  \clist_clear:N \l__foreach_braced_clist
  \seq_map_inline:Nn \l__foreach_arg_seq
   {
    \clist_put_right:Nn \l__foreach_braced_clist { { ##1 } }
   }
  \foreach #1 in~\l__foreach_braced_clist
 }
\ExplSyntaxOff

\parindent0mm

\newcommand{\ItemImage}[5]%
{\pichskip{#5 in}\parpic[#2][#3]{\includegraphics[height=#4 in]{#1.png}}}

\newcommand{\descriptionimage}[6]%
%   #1 image name
% #2
% #3
% #4 image height
% #5
% #6 description in parts: part_1|part_2|...,|part_n
{   \xdef\picturealreadyset{0}%
    \Foreach{\x}[|]{#6}%
    {   \begin{tikzpicture}[overlay, remember picture]%
            \path (current page text area.south);%
            \pgfgetlastxy{\somedummy}{\textbottom}%
            \xdef\bottomofthetext{\textbottom}%             
            \fill[red] (0,0) circle (0.1);%
        \end{tikzpicture}%
        \pgfmathsetmacro{\inchesabovebottom}{-\bottomofthetext/72.27}%
        \pgfmathtruncatemacro{\placepicturenowboolean}{#4 < \inchesabovebottom ? 1 : 0}%
        \ifthenelse{\placepicturenowboolean = 1 \AND \picturealreadyset = 0}%
        {   \ItemImage{#1}{#2}{#3}{#4}{#5}%
            \x%
            \picskip{0}%
            \xdef\picturealreadyset{1}%
        }%
        {\x}%
    }%
    \ifthenelse{\picturealreadyset = 0}%
        {   \begin{tikzpicture}[overlay, remember picture]%
                \path (current page text area.south);%
                \pgfgetlastxy{\somedummy}{\textbottom}%
                \xdef\bottomofthetext{\textbottom}%                         
                \fill[red] (0,0) circle (0.1);
            \end{tikzpicture}%
            \pgfmathsetmacro{\inchesabovebottom}{-\bottomofthetext/72.27}%
            \pgfmathtruncatemacro{\placepicturenowboolean}{#4 < \inchesabovebottom ? 1 : 0}%
            \ifthenelse{\placepicturenowboolean = 1}%
                {   \ItemImage{#1}{#2}{#3}{#4}{#5}\vspace{#4 in}}%
                {   \textcolor{red}{Image not set: didn't fit anywhere. Check dimensions!\\}}%
        }%
        {}% 
}

\newcommand{\sase}[1][2]
{   \foreach \s in {1,...,#1}
    { This is the same old sentence which will be used over and over and over.}
}

\begin{document}

\begin{multicols*}{2}
\textbf{Book Title Number One\\
This is the subtitle of the first book\\
First Name, Last Name}\par
\descriptionimage{book}{sr}{r}{1.25}{.125}{\sase[5]|\sase[5]|\sase[5]|\sase[5]|\sase[5]|\sase[5]}
\par 000-0-00000-000-0,Hb,\$19.95\\
Book Series\\
6 x 9,100,2012\\
\hrule\vspace{0.2in}

\textbf{Book Title Number Two\\
This is the subtitle of the second book\\
First Name,Last Name}\par
\descriptionimage{book}{sr}{r}{1.25}{.125}{\sase[5]|\sase[5]}
\par 000-0-00000-000-0,Hb,\$19.95\\
Book Series\\
10 x 12,500,2012\\
\hrule\vspace{0.2in}
\end{multicols*}

\end{document}

Result

enter image description here