Description: This is a scripting tool. It's purpose is to allow the user to input an unbroken string and have returned a paragraph which has the ends of each line in line with each other. It serves roughly the same function as does the justifying option in Microsoft Word. See the screenshots if you are still confused.
Script Name: Paragraph Formatter
Author: modern algebra
Version: 2.0
Number Of Scripts: 3 (1 required, 2 optional)
Description:
This is a scripting tool. It's purpose is to allow the user to input an unbroken string and have returned a paragraph which has the ends of each line in line with each other. It serves roughly the same function as does the justifying option in Microsoft Word. See the screenshots if you are still confused.
Features
* A nice, easy way to display long strings in a paragraph format
* Easy to add and modify at runtime, allowing for the switching between formatting classes for each situation
* You can write your own formatter or artist classes and use the paragraphing tool to suit your situation.
* Highly customizable.
Screenshots:
Instructions:
As a scripter's tool, it can be quite heavy for non-scripters to use. That is why I wrote a facade for common use of the tool. Naturally, you will still need some scripting knowledge, but the facade allows for this code:
bitmap.draw_paragraph (x, y, max_width, max_height, string)
Where bitmap is the bitmap you are drawing to. This can be self.contents in a window, or any instance of the bitmap class.
It can be used like this, if you want to draw the paragraph in a different way:
formatter =artist = specifications = pg = Paragrapher.new (formatter, artist) text_bitmap = pg.paragraph (string, specifications) bitmap.blt (x, y, text_bitmap, Rect.new (0,0,text_bitmap.width, text_bitmap.height))
Basically, you choose your formatter and artist class at runtime. This means that if you want to use Paragraph::Formatter_2, because you are using a font with set width for all characters, then you would choose that here. Currently, there is only one Artist class, Paragraph::Artist, but of course you can make your own if it does not suit you. You can either specify a bitmap or a fixnum. The fixnum would just be the max width, and the paragrapher would create a bitmap which was at font_size 22, default font name, and it would space each line 32 pixels. With a bitmap, you specify max_width, max_height, font and font size, and anything else that has an effect. Naturally, bitmap in the code is the bitmap you are drawing the paragraph on. If you have any questions, just ask. Also, the text_size method of Bitmap does not, in fact, work properly. In a little while I will post a way to get around this problem as it can get in the way of drawing nice paragraphs.
Put the scripts above main and below the default scripts. For all additional Paragrapher classes, just add them in the Script Editor below the Paragraph Formatter in their own slots, but still above Main. For more pertinent instructions, see the header of the script.
Author's Notes
This script was inspired by Zeriab, and pretty much everything that is good about this script is due to Zeriab. Zeriab deserves more credit for this script then I do, rightly, but since the world isn't just... Anyway, he deserves all my thanks for being an excellent teacher.
Frequently Asked Questions:
N/A
Scripts:
Paragraph Formatter -Required
Spoiler
#============================================================================== # Paragraph Formatter (VX) # Version: 2.0 # Author: modern algebra (rmrk.net) # Date: September 10, 2009 #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # Description: # The idea behind this script is to easily separate a long string into a # paragraph that fits in to the dimensions you specify. More than that, you # can also justify the paragraph #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # Instructions: # For ease of use of people who are not neccesarily interested in writing # their own algorithm, I have included a facade which you can use simply by # this code: # # bitmap.draw_paragraph (x, y, width, height, string) # # where x & y are the x & y coordinates on the specified bitmap, and width # and height are the maximum dimensions of the paragraph and string is the # text you want to display in paragraph form. You can easily change which # formatter or artist classes you want to use with the codes: # # bitmap.paragraph_formatter = Paragrapher::# bitmap.paragraph_artist = Paragrapher:: @max_width next_line (@last_word) end if character == "\n" next_line (i) @format_text.blank_width[-1] = 0 end @last_word = i end end #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Proceed to Next Line # last_word : the index of the beginning of the previous word #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def next_line (last_word) line = @string[@line_break, last_word - @line_break] # Adds current line to f.lines @format_text.lines.push ( line.scan (/./) ) # Calculates the blank space left to cover in the line line_blank = @max_width - @format_text.bitmap.text_size(line).width @format_text.blank_width.push (line_blank.to_f / (line.size.to_f - 1.0) ) # Keeps track of the position in the array of each line @line_break = last_word + 1 end end #============================================================================ # ** Artist #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # Interprets a Formatted Text object and returns a bitmap of the paragraph #============================================================================ class Artist #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Draw # f : Formatted Text Object # justify_text : boolean value on whether to justify text #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def draw (f, justify_text = true) # Calculates the necessary distance between lines line_distance = f.bitmap.height.to_f / f.lines.size.to_f line_distance = [f.bitmap.font.size + 4, line_distance].min # For all lines in the lines array for i in 0...f.lines.size blank_space = f.blank_width[i] position = 0 # For all indices of the line array for j in 0...f.lines[i].size string = f.lines[i][j] tw = f.bitmap.text_size (string).width # Draws the string located at each index f.bitmap.draw_text (position, line_distance*i, tw, line_distance, string) # Keeps track of the position we are in in pixels position += tw position += blank_space if justify_text end end return f.bitmap end end end #======================================================================== # ** Game_System #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Summary of changes: # new instance variables - default_formatter, default_artist # aliased methods - initialize #======================================================================== class Game_System #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Public Instance Variables #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ attr_accessor :default_formatter attr_accessor :default_artist #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Object Initialization #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ alias ma_paragraph_formatter_init initialize def initialize # Run original method ma_paragraph_formatter_init # Initialize original default format and artist classes @default_formatter = Paragrapher::Formatter @default_artist = Paragrapher::Artist end end
Special Codes Formatter - Optional
Spoiler
#============================================================================== # Special Codes Formatter (Addon for Paragraph Formatter 2.0) # Version: 1.0 # Author: modern algebra (rmrk.net) # Date: September 15, 2009 #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # Description: # # This is a formatter and artist combo that interprets and accounts for # various special codes: # # \n - line break to next paragraph (Note the single \, NOT \\) # \\v[x] - Shows the value located in the variable x # \\n[x] - Shows the name of the Actor with ID x # \\c[x] - Changes the colour of the text to x. x can be 0 - 31 # \\c[#hex] - Changes the colour of text to the hex value # \\ - \ # \\pid[x] - Shows Actor ID of Party Member in position X (0-3) # \\nc[x]- Shows the name of class with ID x # \\np[x]- Shows the name of the Party Member with index x # \\ne[x]- Shows the name of Event with ID x on the map # \\nm[x]- Shows the name of Monster with ID x # \\ni[x]- Shows the name of Item with ID x # \\nw[x]- Shows the name of Weapon with ID x # \\na[x]- Shows the name of Armour with ID x # \\pi[x]- Shows the price of Item with ID x # \\pw[x]- Shows the price of Weapon with ID x # \\pa[x]- Shows the price of Armour with ID x # \\iicon[x] - Shows the Icon of Item with ID x # \\wicon[x] - Shows the Icon of Weapon with ID x # \\aicon[x] - Shows the Icon of Armour with ID x # \\icon[x] - Shows the Icon with ID x # \\vocab[value] - prints vocab for that item type. Suitable values are: # level, level_a, hp, hp_a, mp, mp_a, atk, def, spi, # agi, weapon, armor1, armor2, armor3, armor4, weapon1, # weapon2, attack, skill, guard, item, equip, status, save, # game_end, fight, escape, new_game, shutdown, to_title, # continue, cancel, gold # \\f[key] - Show Filter phrase attached to key # \\b - Bold ON # \/b - Bold OFF # \\i - Italic ON # \/i - Italic OFF # \\u - Underline ON # \/u - Underline OFF # \\s - Shadow ON # \/s - Shadow OFF # \\hl[x] - Highlights with color x. \\hl toggles off # \\ac[x]- Shows class of actor with ID x # \\a...[x] - Shows the ... of Actor X. ... can be any of the following: # hp, maxhp, mp, maxmp, atk, def, spi, agi, exp_s, next_exp_s, # next_rest_exp_s, level, weapon_id, armor1_id, armor2_id, # armor3_id, armor4_id - and any other methods from Game_Actor. # \\c - Centres text # \\r - Sets right alignment to text # # It otherwise functions the same as the default Formatter and Artist combo. # The names of the classes are: # # Formatter_SpecialCodes # Artist_SpecialCodes #============================================================================== module Paragrapher #============================================================================ # ** FILTERS #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # FILTERS allows you to set a filter and replace any \F[x] code with it's # corresponding Entry in the Hash. Leave the FILTERS = {} line alone and # below it, you can set all of the codes you will want to be able to put # as an argument in the \F code. #============================================================================ FILTERS = {} FILTERS['PF3'] = '\c[1]Paragraph Formatter\c[0], Version 2.0: Formatter_SpecialCodes' FILTERS[0] = 'Numbered filters work too' #============================================================================ # ** Formatter 3 #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # This class converts a string into a formatted text object, but also # recognizes special message codes. #============================================================================ class Formatter_SpecialCodes " elsif no_args_codes.include? (character) extract_code (character) elsif character == " " char_width = @format_text.bitmap.text_size (character).width if @line_width + char_width + @word_width > @max_width if @line_width == 0 # Really long word! @last_word = i @line_width = @word_width end next_line (@last_word) else @line_width += char_width @line_letter_count += 1 end @line_width += @word_width @line_letter_count += @word_letter_count @word_width = 0 @word_letter_count = 0 @last_word = i elsif character == "\n" # Line break char_width = @format_text.bitmap.text_size (" ").width next_line (@last_word) if @line_width + char_width + @word_width > @max_width @line_width += @word_width @line_letter_count += @word_letter_count next_line (i) # Add in \n independent of system @format_text.lines[-1].push (character) @format_text.blank_width[-1] = 0 @word_width = 0 @last_word = i else # Regular Character @word_width += @format_text.bitmap.text_size(character).width @word_letter_count += 1 if i == @string.size - 1 next_line (@last_word) if @line_width + @word_width > @max_width end end end #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Proceed to Next Line # last_word : the index of the beginning of the previous word #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def next_line (last_word) line = @string[@line_break, last_word - @line_break] # Adds current line to f.lines @format_text.lines.push ( line.scan (/./) ) # Calculates the blank space left to cover in the line line_blank = @max_width - @line_width @format_text.blank_width.push (line_blank.to_f / (@line_letter_count.to_f) ) # Keeps track of the position in the array of each line @line_break = last_word + 1 @line_width = 0 @line_letter_count = 0 end #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Convert Special Characters #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def convert_special_characters (text = @string) return "" if text == nil text = perform_substitution (text) # Get substitutions text.gsub! (/\\C\[(\d+)\]/i) { "\x01" } # Palette Color text.gsub! (/\\C\[#([\dABCDEF]+)\]/i) { "\x01" } # Hex Color text.gsub! (/\\IIC?O?N?\[(\d+)\]/i) { $1.to_i > 0 ? "\x02" : ""} # Item Icon text.gsub! (/\\WIC?O?N?\[(\d+)\]/i) { $1.to_i > 0 ? "\x02" : ""} # Weapon Icon text.gsub! (/\\AIC?O?N?\[(\d+)\]/i) { $1.to_i > 0 ? "\x02" : ""} # Armor Icon text.gsub! (/\\IC?O?N?\[(\d+)\]/i) { "\x02" } # Icon text.gsub! (/\\B/i) { "\x03" } # Bold ON text.gsub! (/\\I/i) { "\x04" } # Italic ON text.gsub! (/\\S/i) { "\x05" } # Shadow ON text.gsub! (/\\U/i) { "\x06" } # Underline ON text.gsub! (/\/B/i) { "\x07" } # Bold OFF text.gsub! (/\/S/i) { "\x09" } # Shadow OFF text.gsub! (/\/I/i) { "\x08" } # Italic OFF text.gsub! (/\/U/i) { "\x10" } # Underline OFF text.gsub! (/\\HL\[(-*\d+)\]/i) { "\x11" } # HighLight text.gsub! (/\\HL/i) { "\x11" } text.gsub! (/\\C/i) { "\x12" } # Align Centre text.gsub! (/\\CENTRE/i) { "\x12" } # Align Centre text.gsub! (/\\RI?G?H?T?/i) { "\x12" } # Align Right return text end #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Perform Substitution #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def perform_substitution (text = @string) text.gsub!(/\\V\[([0-9]+)\]/i) { $game_variables[$1.to_i] } # Variable # FILTERS text.gsub!(/\\F\[["'](.+?)["']\]/i) { FILTERS[$1.to_s] } text.gsub!(/\\F\[(.+?)\]/i) { FILTERS[$1.to_i] } # Party ID to Actor ID while text[/\\PID\[(\d+)\]/i] != nil x = $1.to_i 0 ? $game_actors[$1.to_i].name : ""} # Actor Name text.gsub!(/\\\\/) { "\\" } # New Codes begin text.gsub! (/\\VOCAB\[(\w+)\]/i) { Vocab.send ($1.downcase) } # Vocab rescue end text.gsub! (/\\AC\[(\d+)\]/i) { $game_actors[$1.to_i].class.name } # Actor Class # Actor Stats begin text.gsub! (/\\A([^\[]+?)\[(\d+)\]/i) { $game_actors[$2.to_i].send ($1.to_s.downcase) } rescue end text.gsub! (/\\NC\[(\d+)\]/i) { $1.to_i > 0 ? $data_classes[$1.to_i].name : "" } # Class Name text.gsub! (/\\NE\[(\d+)\]/i) { $1.to_i > 0 ? $game_map.events[$1.to_i].name : "" } # Event Name text.gsub! (/\\NM\[(\d+)\]/i) { $1.to_i > 0 ? $data_enemies[$1.to_i].name : "" } # Monster Name text.gsub! (/\\NI\[(\d+)\]/i) { $1.to_i > 0 ? $data_items[$1.to_i].name : "" } # Item Name text.gsub! (/\\NW\[(\d+)\]/i) { $1.to_i > 0 ? $data_weapons[$1.to_i].name : "" } # Weapon Name text.gsub! (/\\NA\[(\d+)\]/i) { $1.to_i > 0 ? $data_armors[$1.to_i].name : "" } # Armor Name text.gsub! (/\\PI\[(\d+)\]/i) { $1.to_i > 0 ? $data_items[$1.to_i].price.to_s : "" } # Item Price text.gsub! (/\\PW\[(\d+)\]/i) { $1.to_i > 0 ? $data_weapons[$1.to_i].price.to_s : "" } # Weapon Price text.gsub! (/\\PA\[(\d+)\]/i) { $1.to_i > 0 ? $data_armors[$1.to_i].price.to_s : "" } # Armor Price text.gsub! (/\\V\[([0-9]+)\]/i) { $game_variables[$1.to_i] } # Variable return text end #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Extract Code # code : the code to extract #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def extract_code (code) case code when "\x02" @word_letter_count += 1 @word_width += 24 when "\x03" then @format_text.bitmap.font.bold = true # Bold when "\x04" then @format_text.bitmap.font.italic = true # Italic when "\x05" then @format_text.bitmap.font.shadow = true # Shadow when "\x07" then @format_text.bitmap.font.bold = false # Bold when "\x08" then @format_text.bitmap.font.italic = false # Italic when "\x09" then @format_text.bitmap.font.shadow = false # Shadow end end #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Argument Codes #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def no_args_codes return ["\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\x09", "\x10"] end #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Argument Codes #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def args_codes return ["\x01", "\x02", "\x11", "\x12"] end end #============================================================================ # ** Artist 2 #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # This is an artist class designed to recognize some special message codes. #============================================================================ class Artist_SpecialCodes #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Draw # f : Formatted Text Object # justify_text : boolean value on whether to justify text #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def draw (f, justify_text = true) @f = f @justify_text = justify_text @highlight = -1 @underline = false # Calculates the necessary distance between lines line_distance = @f.bitmap.height.to_f / @f.lines.size.to_f @line_distance = [@f.bitmap.font.size + 4, line_distance].min # For all lines in the lines array for i in 0...@f.lines.size # Compose line into a single string @text = "" @f.lines[i].each { |char| @text += char } @blank_space = @f.blank_width[i] @centre = @text[/\x12/] != nil @right = @text[/\x12/] != nil && !@centre total_blank = 0 if @centre || @right @real_bitmap = @f.bitmap.dup @f.bitmap = Bitmap.new (@real_bitmap.width, @line_distance) @f.bitmap.font = @real_bitmap.font.dup @y = 0 else @y = i*@line_distance end @x = 0 # For all indices of the line array loop do c = @text.slice!(/./m) break if c.nil? interpret_string (c) end # Align Text if @centre || @right blank = (@real_bitmap.width - @x) blank /= 2 if @centre rect = Rect.new (0, 0, @real_bitmap.width, @line_distance) @real_bitmap.blt (blank, i*@line_distance, @f.bitmap, rect) @real_bitmap.font = @f.bitmap.font.dup @f.bitmap.dispose @f.bitmap = @real_bitmap end end return @f.bitmap end #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Get Text Color # n : Text color number (0-31) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def text_color(n) x = 64 + (n % 8) * 8 y = 96 + (n / 8) * 8 windowskin = Cache.system ("Window") return windowskin.get_pixel(x, y) end #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Draw Icon # icon_index : Icon number # x,y : draw spot coordinates #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def draw_icon(icon_index, x, y) bitmap = Cache.system("Iconset") rect = Rect.new(icon_index % 16 * 24, icon_index / 16 * 24, 24, 24) @f.bitmap.blt(x, y, bitmap, rect) end #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Interpret Character # char : the char to decode #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def interpret_string (char) case char when "\x01" # Colour @text.slice! (//i) if $1.include? ("#") r, g, b = $1[1, 2].to_i (16), $1[3, 2].to_i (16), $1[5, 2].to_i (16) @f.bitmap.font.color = Color.new (r, g, B) else @f.bitmap.font.color = text_color ($1.to_i) end when "\x02" # Icon @text.slice! (//) draw_icon ($1.to_i, @x, @y) @x += 24 @x += @justify_text && !@centre && !@right ? @blank_space : 0 when "\x03" then @f.bitmap.font.bold = true # Bold ON when "\x04" then @f.bitmap.font.italic = true # Italic ON when "\x05" then @f.bitmap.font.shadow = true # Shadow ON when "\x06" then @underline = true # Underline ON when "\x07" then @f.bitmap.font.bold = false # Bold OFF when "\x08" then @f.bitmap.font.italic = false # Italic OFF when "\x09" then @f.bitmap.font.shadow = false # Shadow OFF when "\x10" then @underline = false # Underline OFF when "\x11" # Highlight @text.slice! (//) @highlight = $1.to_i when "\x12" # Centre or Right @text.slice! (//) when "\n" # Draw nothing when blank space else draw_character (char) end end #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Draw Character # string : the string to draw #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def draw_character (string) tw = @f.bitmap.text_size (string).width ls = @justify_text && !@centre && !@right ? @blank_space : 0 hl_rect = Rect.new (@x, @y, tw + ls, @line_distance) # Draw Highlight if @highlight.between? (0, 31) colour = text_color (@highlight) colour.alpha = 120 contents.fill_rect (hl_rect, colour) end # Draw Underline if @underline y = @y + @line_distance - 2 @f.bitmap.fill_rect (@x, y, hl_rect.width, 2, @f.bitmap.font.color) end # Draws the string located at each index @f.bitmap.draw_text (@x, @y, tw, @line_distance, string) # Keeps track of the position we are in in pixels @x += tw + ls end end end
Paragrapher::Formatter 2 - Optional
Spoiler
#============================================================================== # ** Paragrapher::Formatter 2 (Using Zeriab's Algorithm) #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # This algorithm was written by Zeriab for fonts which have characters of # the same width. This is like Courier New, UMEGothic and fonts of that sort. # This algorithm attaches a cost to each line based on the amount of white # space at the end of that line. It will display the way of writing the text # with the lowest total cost. In prcatice, this will mean that it will, as # much as possible, reduce the spacing between letters in a line and make # the spacing more consistent for each line of the paragraph #============================================================================== module Paragrapher class Formatter_2 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Format #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def format (string, specifications) f = Formatted_Text.new f.lines, f.blank_width, word_lengths, words = [], [], [], [] tracker = 0 for i in 0...string.size if string[i,1] == " " || i == string.size - 1 if i == string.size - 1 i += 1 end word_lengths.push (i - tracker) words.push (string[tracker, i - tracker]) tracker = i + 1 end end if specifications.class == Bitmap max_width = specifications.width f.bitmap = specifications elsif specifications.class == Fixnum || specifications.class == Float max_width = specifications f.bitmap = Bitmap.new (1,1) else # Error Catching: Bad specification bitmap = Bitmap.new (200, 64) f = format ('Specifications Error', bitmap) p 'Specifications Error: Please Pass Fixnum, Float or Bitmap' return f end tw = f.bitmap.text_size('a').width max_width = [max_width / tw, 180].min # Error Catching: Word too long if word_lengths.max > max_width f = format ('Too long' , specifications) p 'One or more words is too long for specified width' return f end position = line_break (word_lengths, max_width) lines = give_lines (position, position.size - 1, words) max_width *= tw for i in 0...lines.size line = lines[i] f.lines.push (line.scan (/./)) if i == lines.size - 1 f.blank_width.push (0) else text_width = line.size * tw extra_space = max_width - text_width f.blank_width.push (extra_space.to_f / (line.size.to_f - 1.0)) end end if f.bitmap != specifications f.bitmap = Bitmap.new (max_width, f.lines.size*32) end return f end #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # * Line Break (written by Zeriab) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def line_break(word_lengths, max_length) return false if max_length > 180 word_lengths.unshift(nil) extra_spaces = Table.new(word_lengths.size,word_lengths.size) line_prices = Table.new(word_lengths.size,word_lengths.size) word_price = [] position = [] inf = max_length*max_length + 1 for i in 1...word_lengths.size extra_spaces[i,i] = max_length - word_lengths[i] for j in (i+1)..[word_lengths.size-1, max_length/2+i+1].min extra_spaces[i,j] = extra_spaces[i,j-1] - word_lengths[j]-1 end end for i in 1...word_lengths.size for j in i..[word_lengths.size-1, max_length/2+i+1].min if extra_spaces[i,j] = 0 line_prices[i,j] = 0 else line_prices[i,j] = extra_spaces[i,j]*extra_spaces[i,j] end end end word_price[0] = 0 for j in 1...word_lengths.size word_price[j] = inf for ik in 1..j i = j - ik + 1 break if line_prices[i,j] == inf if word_price[i-1] + line_prices[i,j]