I have recently come to the realization that the text_size method of Bitmap sometimes does not give an accurate estimation of the actual text size in all fonts, and this is because it does not give an accurate estimation of specific letters. For this reason I wrote a little script which will display all commonly used characters and show you which letters are not accurately documented, so that you can modify the text size method in order to fix this for each font you use. For instance, when you put the font Arial at Font 22 through the script, it returns this:
(https://rmrk.net/proxy.php?request=http%3A%2F%2Fimg88.imageshack.us%2Fimg88%2F8526%2Fdemopictureqy1.png&hash=e25431a42a929f33dbb995475aa715f1dcc2ab2c)
and that shows that the text_sixe returned for A, V, f, v. w, /, and \ is inaccurate. Then, all you need to do is alias text_size and adjust the width returned when the font is Arial. Anyone who wants to use it can, I've attached the demo below and I wrote up the script pretty quickly so it's kind of ugly:
class Window_Test < Window_Base
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
def initialize
super (0,0,640,160)
self.contents = Bitmap.new (width - 32, height - 32)
end
#--------------------------------------------------------------------------
# * Refresh
#--------------------------------------------------------------------------
def refresh
self.contents.clear
self.contents.font.name = 'Arial'
self.contents.font.size = 22
x = 0
# Draw all numbers
for i in 0...10
colour = Color.new ((i*80)%255, (i*60)%255, (i*100)%255, 130)
ts = self.contents.text_size (i.to_s)
bitmap = Bitmap.new (ts.width, ts.height)
bitmap.font = self.contents.font
bitmap.fill_rect (0,0,ts.width, ts.height, colour)
bitmap.draw_text (0,0,ts.width,ts.height, i.to_s)
self.contents.blt (x, 0, bitmap, Rect.new (0,0,ts.width, ts.height))
x += ts.width + 3
end
x = 0
x2 = 0
# Draw all letters
for i in 65...91
# Get a random color
colour = Color.new ((i*20)%255, (i*30)%255, (i*60)%255, 100)
letter = i.chr
# For all uppercase letters
ts = self.contents.text_size (letter)
bitmap = Bitmap.new (ts.width, ts.height)
bitmap.font = self.contents.font
bitmap.fill_rect (0,0,ts.width, ts.height, colour)
bitmap.draw_text (0,0,ts.width,ts.height, letter)
self.contents.blt (x, 32, bitmap, Rect.new (0,0,ts.width, ts.height))
x += ts.width + 3
# For all lowercase letters
letter.downcase!
ts = self.contents.text_size (letter)
bitmap = Bitmap.new (ts.width, ts.height)
bitmap.font = self.contents.font
bitmap.fill_rect (0,0,ts.width, ts.height, colour)
bitmap.draw_text (0,0,ts.width,ts.height, letter)
self.contents.blt (x2, 64, bitmap, Rect.new (0,0,ts.width, ts.height))
x2 += ts.width + 3
end
# For other common characters
others = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '=',
'+', "'", '"', ';', ':', '/', '?', '.', '>', '<', ',', '`',
'~', '\\', '|']
x = 0
for i in 0...others.size
char = others[i]
colour = Color.new ((i*20)%255, (i*30)%255, (i*60)%255, 100)
ts = self.contents.text_size (char)
bitmap = Bitmap.new (ts.width, ts.height)
bitmap.font = self.contents.font
bitmap.fill_rect (0,0,ts.width, ts.height, colour)
bitmap.draw_text (0,0,ts.width,ts.height, char)
self.contents.blt (x, 96, bitmap, Rect.new (0,0,ts.width, ts.height))
x += ts.width + 3
end
end
end
In any case, the problem I am encountering is whether it is worth it to write a new text_size method or just go with this. It works perfectly fine, but I am trying to think of a nice way to make a more accurate text size. So far my best idea is to make a large bitmap, draw it, then analyze each pixel row of the bitmap for the amount of pixels that have a colour other then default. It will add up the amount in each row, and then just take the row with the most non-default colors and set that as the width. However, I was wondering if anyone knew a better way then that. How does the regular text_size method function? Would my way be too time-consuming? And is it worth it given that I can use what I have already written to manually adjust for the fonts I use? Also, would my method even work properly?
I would change the code to this:
class Window_Test < Window_Base
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
def initialize
super (0,0,640,160)
self.contents = Bitmap.new (width - 32, height - 32)
end
#--------------------------------------------------------------------------
# * Refresh
#--------------------------------------------------------------------------
def refresh
self.contents.clear
self.contents.font.name = 'Arial'
self.contents.font.size = 22
# self.contents.font.italic = true
x = 0
# Draw all numbers
for i in 0...10
colour = Color.new ((i*80)%255, (i*60)%255, (i*100)%255, 130)
ts = self.contents.text_size (i.to_s)
ts.width += 3
bitmap = Bitmap.new (ts.width, ts.height)
bitmap.font = self.contents.font
bitmap.fill_rect (0,0,ts.width, ts.height, colour)
bitmap.draw_text (0,0,ts.width,ts.height, i.to_s)
self.contents.blt (x, 0, bitmap, Rect.new (0,0,ts.width, ts.height))
x += ts.width
end
x = 0
x2 = 0
# Draw all letters
for i in 65...91
# Get a random color
colour = Color.new ((i*20)%255, (i*30)%255, (i*60)%255, 100)
letter = i.chr
# For all uppercase letters
ts = self.contents.text_size (letter)
ts.width += 3
bitmap = Bitmap.new (ts.width, ts.height)
bitmap.font = self.contents.font
bitmap.fill_rect (0,0,ts.width, ts.height, colour)
bitmap.draw_text (0,0,ts.width,ts.height, letter)
self.contents.blt (x, 32, bitmap, Rect.new (0,0,ts.width, ts.height))
x += ts.width
# For all lowercase letters
letter.downcase!
ts = self.contents.text_size (letter)
ts.width += 3
bitmap = Bitmap.new (ts.width, ts.height)
bitmap.font = self.contents.font
bitmap.fill_rect (0,0,ts.width, ts.height, colour)
bitmap.draw_text (0,0,ts.width,ts.height, letter)
self.contents.blt (x2, 64, bitmap, Rect.new (0,0,ts.width, ts.height))
x2 += ts.width
end
# For other common characters
others = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '=',
'+', "'", '"', ';', ':', '/', '?', '.', '>', '<', ',', '`',
'~', '\\', '|']
x = 0
for i in 0...others.size
char = others[i]
colour = Color.new ((i*20)%255, (i*30)%255, (i*60)%255, 100)
ts = self.contents.text_size (char)
ts.width += 3
bitmap = Bitmap.new (ts.width, ts.height)
bitmap.font = self.contents.font
bitmap.fill_rect (0,0,ts.width, ts.height, colour)
bitmap.draw_text (0,0,ts.width,ts.height, char)
self.contents.blt (x, 96, bitmap, Rect.new (0,0,ts.width, ts.height))
x += ts.width
end
end
end
This way you will also solve the problem of the text_size not giving correct values for italicized text. (It explicitly states in the help file that text_size "Does not include the angled portions of italicized text.")
However the idea of adding +3 to the width seems to fix the problem. (I tried with font size 72, still look fine.)
The prize of this fix is slightly larger bitmaps. But it is surely much faster than any image analysis used on the bitmaps to figure out whether the letter has been squeezed or not.
It seems that you seek a general purpose solution for this. I am afraid there is no general solution.
If you really want to have one instead of just running this test to fiddle the font size correctly I suggest utilize the Factory Method pattern. Though you have to consider that the text_size method most likely has been written in C. This means that almost no matter what you do it will have a negative effect efficiency wise.
I am afraid that you will have to analyze the given string. A process that might be costly.
Though if implemented correctly all you have to do would be to create a subclass of the Creator for each font. It will require some fiddling around with each font you are to use. You can just use the script you put in here. All it takes is some time.
What I can't answer is whether such a solution would be too costly. As a guideline you can say that as long as the speed is insignificant compared to the time of drawing the text on the bitmap you are fine.
Yeah, I was looking for the general solution, but I think it will probably be best to just use my original thought and just make exceptions for each font used. I don't want to add a set number to the width simply because exactness is pretty key in the situation I want to use it in, specifically as displaying text as the usser is typing. This is simply because it draws character by character, which means whatever constant I add it to will add that constant between each letter. It would also make the Paragraph Formatter look a little goofy. Thanks for the advice, and the revision.
This seems super nice and helpful! ;8
Notice that I use up the space you have placed between the letters ^_^
That way you won't have problems with letters overlapping.
Anyway, you could always use a fixed-width font :P
This is basically what I am considering doing:
#------------------------------------------------------------------------
# * Text Size (Aliased for 'Arial' Font exception)
#------------------------------------------------------------------------
alias arial_font_exception_size text_size
def text_size (string)
# Get original text size
rect = arial_font_exception_size (string)
if self.font.name == 'Arial'
# Set all inaccurate letters in an array
bad_letters == ['A', 'V', 'f', 'v', 'w', '/', "\\"]
for i in 0...string.size
if bad_letters.include? (string[i,1])
# Add 1 to the width for each of the offending characters in the string
rect.width += 1
# Add an additional pixel for each f
rect.width += 1 if string[i,1] == 'f'
end
end
end
return rect
end
And when the Paragraph Formatter is ready and the Journal is ready, I will just give them the script I wrote here and instruct them in writing codes like this to add to make the widths appropriate. I think that will probably be the quickest way to get a correct text_size. Maybe I should use a Table :P
You have an error in that text: 'bad_letters == ['A', 'V', 'f', 'v', 'w', '/', "\\"]'
Should be 'bad_letters = ['A', 'V', 'f', 'v', 'w', '/', "\\"]'
Nah, don't use a Table.
Instead you should use a binary search tree.
Considering how few bad letters there are this might simply be an overkill.
Another thing you didn't include was if the font were italicized. In that case you should add +2 to the width regardless of them being bad letters or not.
Ah, yeah, I forgot about italicized. Thanks for reminding me. And yeah, I figured out the problem with bad_letters when I tested and it threw me an uninitialized local variable error. Thanks for noticing that though.
I think I'll just add in the line:
rect.width += 2*string.size if self.font.italic
Thanks for reminding me