1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
|
#!/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 <cs@taskjuggler.org>
#
# 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
|