prawn icon indicating copy to clipboard operation
prawn copied to clipboard

fix float precision bug in text box rendering

Open unterkoefler opened this issue 2 years ago • 2 comments

fixes #1292

unterkoefler avatar Feb 16 '23 22:02 unterkoefler

I still don't quite understand the root cause of the issue.

pointlessone avatar Mar 09 '23 09:03 pointlessone

I still don’t quite understand the root cause of the issue.

When we render a text box with shrink_to_fit enabled, we call wrap twice. The first time is to check if the text will fit in the box. If it doesn’t fit, the font size is reduced and we try again. When that’s done, the text should fit in the box. Then we call wrap again to actually draw the text.

In wrap, the check to see if the text will fit in one line without wrapping to the next is this: if @accumulated_width + segment_width <= @width

The bug occurs specifically when the LHS @accumulated_width + segment_width (the entire width of the line) is equal to @width (the width of the text box). The first time through when we’re trying to figure out if the font is small enough, the float math works out such that width of the line <= width of the text box. Everything stays on the same line and shrink_to_fit says that it will fit at the current font size.

Now, wrap runs again, this time to do the actual rendering. This time, because float math is non-deterministic, @accumulated_width + segment_width is not <= @width. It’s very very slightly bigger. So now we think that the text won’t fit on one line, so the last word gets wrapped onto the next line. If the text box is too short, the second line doesn’t get rendered.

The assumption being violated here is that subsequent calls to wrap will produce text laid out the same way.

Also, regardless, @accumulated_width + segment_width <= @width is a problem because it’s comparing floats for equality without a delta.

unterkoefler avatar Mar 09 '23 17:03 unterkoefler