The following example gives me overlapping figures (figures overlapping themselves and figures overlapping the text) as can be seen in the image below.
I can work around this if I change the surrounding text, the image sizes or remove the h
-placement modifier of the second figure. Also with using \raggedbottom
the overlapping disappears. The example gives an underfull vbox warning, but on other pages where these warnings occur latex just strechtes the vertical skips so that it doesn't look that good anymore, but at least it's still readable.
So why is this happening? And is there a way to fix this without rewriting some text or not placing the figure "here"?
\documentclass[parskip=half]{scrbook}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\begin{document}
\begin{figure}[th]
\fbox{\parbox[c][6cm]{7cm}{img 1}}
\caption{caption 1}
\end{figure}
x\\x\\x\\x\\x\\x\\x\\x\\x\\x\\x
\begin{figure}[th]
\raggedleft
\fbox{\parbox[c][4cm]{5cm}{img 2}}
\caption{caption 2}
\end{figure}
\section{A section}
y\\y\\y\\y\\y
\end{document}
Best Answer
What a fascinating problem!
As it turns out this is (probably) a bug in LaTeX in there since the dawn of time, but as it touches so delicate areas (where TeX has its limitations) I'm not sure there could be a safe cure for it even if we want to attempt it --- I offer a solution later on, but that may have other issues I haven't thought about yet.
So what is the problem? We need some very special circumstances in the first place
scrbook
with optionparskip=half
is essentialh
float needs to come at the very bottomIt is possible to shorten the MWE even further and that helped me to pinpoint the issue:
The most important line I added was
which gives us tracing of the page breaking. If we run that we get
This shows how the first here float is added
c=-10003
then there are all the lines withx
on them until we havewhich corresponds to the second here float. At that point the page is rebuild so we see all the lines coming up again and then ending in this:
The first of those is the last
x
line then there is a big jump int=
due to the fact that we got the second here float added and then att=526.12756
we see ap=-300
, i.e., our explicit penalty. We don't see the space of3cm
because\addpenalty
moves itself in front of a preceding skip.The final line then has
c=*
meaning we have too much material (that's now the 3cm added). So the page break is taken at the explicit penalty on the line before, i.e., atNow if you look at this tracing output (and you have your TeXbook ready, or the LaTeX Companion or you know these things by heart :-) ) then you will notice why the page comes out as it does ... do you? No?
Ok here we go:
t=526.12756
but the page goals isg=595.80026
so we are far too short and the page material needs to stretch;plus -0.8
is actually negative on this line (and only this line) so in order to stretch from526pt
to595pt
we have to multiply this by-86
(roughly);-86
;12pt plus 2pt minus 2pt
, so if its plus part is multiplied-86
we get12pt plus -172pt
so a skip of-150pt
in total and that drives the lines ofx
-es upwards;-150pt
and this is what you send up with.So the question that remains is why do we end up with this negative "plus part" on this very line?
The answer to this is deeply hidden in a macro called
\@addcurcol
whose task it is to attach a float in its proper place. In case of a "here float" it will execute this code:At its very end it issues in certain circumstances (when the float was encountered in vertical mode)
\vskip - \parskip
and with\parskip
being6.8pt plus 6.8pt
due to the option toscrbook
we end up withand with the rest of the plus parts of on the page totally to
6pt
(you can see that in the tracing above) we get theplus -0.8
overall ... and there we are ... sigh.So what goes wrong here? This negative
\parskip
is added after the here float to account for the fact that another\parskip
will be added soon and if we don't cancel it out the spacing will look very uneven --- especially so if you have a large\parskip
in the first place like in the example.However, in our example we end up getting a page break after it, so there isn't any parskip following that needs canceling and so we have this extra negative one that really messes up the page calculations.
In other words, the page break should never happen there but before that negative
\parskip
or in fact even before the\intextsep
following the here float. But this is quite difficult to arrange. Something like\addpenalty
or\addvspace
never gets to see this space (as it is added inside the output routine and when the output routine ends you will get automatically a\penalty 10000
added at the very end and that hides anything before.So I tried a different approach:
\intextsep
or\intextsep - \parskip
) inside the output routine;\vskip
only after the output routine has ended by pushing it out using\aftergroup
.So applying this to the original MWE we get:
And the result is
No guarantee though that this isn't changing other documents because that now changes the spacing after here floats near the end of a page.