#!/usr/bin/env ruby -w # encoding: UTF-8 # # = TextFormatter.rb -- The TaskJuggler III Project Management Software # # Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 # by Chris Schlaeger # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # require 'taskjuggler/UTF8String' class TaskJuggler # This class provides a simple text block formatting function. Plain text # can be indented and limited to a given text width. class TextFormatter attr_accessor :indentation, :width, :firstLineIndent def initialize(width = 80, indentation = 0, firstLineIndent = nil) # The width of the text including the indent. @width = width # The indent for the first line of a paragraph @firstLineIndent = firstLineIndent || indentation # The indent for other lines. @indentation = indentation end # Add @indentation number of spaces at the beginning of each line. The # first line will be indented by @firstLineIndent. Lines that are longer # than @width will be clipped. def indent(str) out = '' # Indentation to be used for the currently processed line. It will be # set to nil if it was inserted already. indentBuf = ' ' * @firstLineIndent linePos = 0 # Process the input String from start to finish. str.each_utf8_char do |c| if c == "\n" # To prevent trailing white spaces we only insert a line break # instead of the indent buffer. if indentBuf out += "\n" end # The indent buffer for the next line. indentBuf = "\n" + ' ' * @indentation else # If we still have an indent buffer, we need to insert it first. if indentBuf out += indentBuf linePos = indentBuf.delete("\n").length indentBuf = nil end # Discard all characters that extend of the requested line width. if linePos < @width out << c linePos += 1 end end end # Always end with a line break out += "\n" unless out[-1] == "\n" out end # Format the String _str_ according to the class settings. def format(str) # The resulting String. @out = '' # The column of the last character of the current line. @linePos = 0 # A buffer for the currently processed word. @wordBuf = '' # True of we are at the beginning of a line. @beginOfLine = true # A buffer for the indentation to be used for the next line. @indentBuf = ' ' * @firstLineIndent # The status of the state machine. state = :beginOfParagraph # Process the input String from start to finish. str.each_utf8_char do |c| case state when :beginOfParagraph # We are currently a the beginning of a new paragraph. if c == ' ' || c == "\n" # ignore it else # A new word started. @wordBuf << c state = :inWord end when :inWord # We are in the middle of processing a word. if c == ' ' || c == "\n" # The word has ended. appendWord state = c == ' ' ? :betweenWords : :betweenWordsOrLines elsif c == "\r" # CR is used to start a new line but without starting a new # paragraph. appendWord @indentBuf = "\n" + ' ' * @firstLineIndent @beginOfLine = true state = :betweenWords else # Add the character to the word buffer. @wordBuf << c end when :betweenWords # We are in between words. if c == ' ' # ignore it elsif c == "\n" state = :betweenWordsOrLines else # A new word started. @wordBuf << c state = :inWord end when :betweenWordsOrLines if c == "\n" # The word break is really a paragraph break. @indentBuf = "\n\n" + ' ' * @firstLineIndent @beginOfLine = true state = :beginOfParagraph elsif c == ' ' state = :betweenWords else @wordBuf << c state = :inWord end else raise "Unknown state in state machine: #{state}" end end # Add any still pending word. appendWord # Always end with a line break @out += "\n" unless @out[-1] == "\n" @out end private def appendWord # Ignore empty words. wordLength = @wordBuf.length return unless wordLength > 0 # If the word does not fit into the current line anymore, we have to # start a new line. @beginOfLine = true if @linePos + 1 + wordLength > @width if @beginOfLine # Insert the content of the @indentBuf and reset @linePos and # @indentBuf. @out += @indentBuf @linePos = @indentBuf.delete("\n").length @indentBuf = "\n" + ' ' * @indentation else # Insert a space to separate the words. @out += ' ' @linePos += 1 end # Append the word and reset the @wordBuf. @out += @wordBuf @wordBuf = '' @linePos += wordLength @beginOfLine = false if @beginOfLine end end end