Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
:valign=>:center or :bottom in text_box slightly inaccurate #169
When you use :valign=>:bottom in Text::Box, the resulting text isn't quite at the bottom, and there's a similar problem with :valign=>:center. Of course, you're always at the mercy of the font metrics, but after a quick read through the code it sure looks like a bug in Text:Box.
Text::Box ends up calculating the height as the number of (wrapped) lines times the line height. That's the correct amount to advance down the page, but it's actually slightly bigger than the actual extent of the text because for most fonts ascender+descender is measurably smaller than the line height, and so on the final line of text nLines*line_height is a bit too big. Obviously this is most noticeable with one-line text boxes.
The pastie http://pastie.org/1294734 is an example, just drop it in prawn/examples/text and run it to see that this effect is not microscopic; it can easily be 2-3 points away from the expected position. It also demonstrates a way to fix it by providing a subclass of Box which overrides process_vertical_alignment... though hopefully this fix can make its way into Prawn in some form!
pushed a commit
Apr 20, 2011
@derek-watson I'm not sure why this issue was closed in the first place, but we can definitely re-open if the issue is still present.
Because 1.0.0.rc2 is a fairly old release, I'd want to be sure we can reproduce this either in the latest release (0.14.0), or even better, on master. Please confirm that, and I'll re-open.
If you want to move things along even further, you can try preparing a pull request with the fix and a test. A commit from 3 years ago might merge cleanly, but it's just as likely that it'll need to be changed somewhat if the underlying code has changed.
Right now there are many open issues in Prawn, and I'm currently the only active maintainer. The more you can do to help me prep this fix for release, the sooner it will get taken care of.
referenced this issue
Jan 24, 2014
Updating this ticket with a fresh example and screenshot of current behavior on master:
require_relative "lib/prawn" Prawn::Document.generate('vertical_align.pdf') do text_box "The rain in spain falls mainly in the plains.", :at => [100,450], :width => 100, :height => 100, :size => 16 stroke_rectangle [100, 450], 100, 100 text_box "The rain in spain falls mainly in the plains.", :at => [250,450], :width => 100, :height => 100, :valign => :center, :size => 16 stroke_rectangle [250, 450], 100, 100 text_box "The rain in spain falls mainly in the plains.", :at => [400,450], :width => 100, :height => 100, :valign => :bottom, :size => 16 stroke_rectangle [400, 450], 100, 100 end
I think we can agree that the top align looks correct. It's the centering and bottom align that has some complications to it.
For center valign: it looks like we're currently making the gap between the top and the ascender equal to the gap between the bottom and the descender. Looking at how some other software does it, it looks like what we should be doing is keeping the gap between ascender and the top of the box equal to that of the baseline and the bottom of the box.
I propose that we go ahead and change the behavior to compute the bottom gap based on the baseline
For bottom valign:, it looks like we're including the line gap. Looking for examples elsewhere, some things I found do this, while others do not. Prawn's text box actually has an option for choosing what behavior you want for this (
I propose we keep behavior as-is when
Will these changes make everyone happy? If not, let me know where the problems are.
/cc @bradediger, who hopefully knows more about this stuff than I do.
Unfortunately not. @Bluejade is the text box expert, and I have only interacted with that code tangentially due to my work on tables. Sorry to pass the buck but I have no direct knowledge here.
@sandal: Your assessment of the problem and proposed solutions seem spot-on to me. The lower alignment example seems to be a defect; the center alignment is potentially debatable in that a different example with heavier descenders on the last line might make the box look more balanced. I don't know of any literature on the topic, but it does make intuitive sense that the eye matches the ascender of the first line with the baseline of the last line, not its descender.
Count me as a
@sandal There is already a
The final gap can be removed by (in
when :bottom @at = @at - (@height - height) + @descender @at -= line_gap end
I was able to implement the fixes to
@sandal Here's a few examples of the implemented fixes:
Notice here how the center aligned element has top/bottom gaps equal to the distance from the bottom to the baseline.
Here we can see that the bottom aligned element does not have a final gap between the box bottom and the descender.
I'll submit a pull request shorty. Let me know what you think!
@jessedoyle: Thanks. I think we still need
The default for
It'd apply to both
I'm trying to think through how
@sandal I'm not sure I agree with your last comment.
If you set
In this case, there would always be an 'initial gap', which consists of the empty space at the top of the box.
This seems to be logical to me, even if the font size is reduced due to
@jessedoyle: Think of the initial gap and final gap as simply being a top and bottom margin, and then we're trying to vertically align the whole box.
If we ignore
I'm wondering if we need to think a little more on all of this. I'm having trouble thinking of when you would want to set
But if we were to offer finer grained control, we might want to implement something like
What do you think?
added a commit
Oct 15, 2014
@sandal: I definitely understand you now.
I agree that there should be some more thought on this.
Honestly, I don't think an
Furthermore, my efforts to implement the
Here's my current code for
def process_vertical_alignment(text) return if defined?(@vertical_alignment_processed) && @vertical_alignment_processed @vertical_alignment_processed = true return if @vertical_align == :top wrap(text) case @vertical_align when :center @at -= (@height - height + @descender) * 0.5 when :bottom @at -= (@height - height) @at += line_gap if @final_gap end @height = height end
You can change the code to:
def process_vertical_alignment(text) return if defined?(@vertical_alignment_processed) && @vertical_alignment_processed @vertical_alignment_processed = true wrap(text) case @vertical_align when :top # Do something if @inital_gap when :center @at -= (@height - height + @descender) * 0.5 when :bottom @at -= (@height - height) @at += line_gap if @final_gap end @height = height end
But this causes tests to break, likely due to not returning before the
@jessedoyle: Thanks for the work you've done on this so far, it's a huge help.
The notion that we include a line gap at the bottom of the text box by default but not at the top is almost certainly a historical accident, and so I'm willing to consider this to be a bug, because it's never going to be desirable behavior.
With that in mind, I think we should figure out what the correct behavior is:
I'm in favor of the first approach (supporting all forms of padding) because it's going to be the most natural user experience. Unfortunately, it's also the most complex, and it may mean that I'd need to write this patch myself unless you're up for doing a lot of rework.
The original suggestions I made about tweaking
@jessedoyle It seems like the work you've done gets us close to option 2. Since it's going to be a pre-requisite for implementing option 1 anyway, maybe you can revise your pull request to simply act as if