/usr/lib/ruby/vendor_ruby/text/format.rb is in ruby-text-format 1.0.0-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 | # :title: Text::Format
# :main: Text::Format
#--
# Text::Format for Ruby
# Version 1.0.0
#
# Copyright (c) 2002 - 2005 Austin Ziegler
#
# $Id: format.rb,v 1.5 2005/04/20 01:43:55 austin Exp $
#++
unless defined?(Text)
module Text; end
end
# = Introduction
#
# Text::Format provides the ability to nicely format fixed-width text with
# knowledge of the writeable space (number of columns), margins, and
# indentation settings.
#
# Copyright:: Copyright (c) 2002 - 2005 by Austin Ziegler
# Version:: 1.0.0
# Based On:: Perl
# Text::Format[http://search.cpan.org/author/GABOR/Text-Format0.52/lib/Text/Format.pm],
# Copyright (c) 1998 Gábor Egressy
# Licence:: Ruby's, Perl Artistic, or GPL version 2 (or later)
#
class Text::Format
VERSION = '1.0.0'
SPACES_RE = %r{\s+}mo.freeze
NEWLINE_RE = %r{\n}o.freeze
TAB = "\t".freeze
NEWLINE = "\n".freeze
# Global common English abbreviations. More can be added with
# #abbreviations.
ABBREV = %w(Mr Mrs Ms Jr Sr Dr)
# Formats text flush to the left margin with a visual and physical
# ragged right margin.
#
# >A paragraph that is<
# >left aligned.<
LEFT_ALIGN = :left
# Formats text flush to the right margin with a visual ragged left
# margin. The actual left margin is padded with spaces from the
# beginning of the line to the start of the text such that the right
# margin will be flush.
#
# >A paragraph that is<
# > right aligned.<
RIGHT_ALIGN = :right
# Formats text flush to the left margin with a visual ragged right
# margin. The line is padded with spaces from the end of the text to the
# right margin.
#
# >A paragraph that is<
# >right filled. <
RIGHT_FILL = :fill
# Formats the text flush to both the left and right margins. The last
# line will not be justified if it consists of a single word (it will be
# treated as +RIGHT_FILL+ in this case). Spacing between words is
# increased to ensure that the textg is flush with both margins.
#
# |A paragraph that|
# |is justified.|
#
# |A paragraph that is|
# |justified. |
JUSTIFY = :justify
# When #hard_margins is enabled, a word that extends over the right
# margin will be split at the number of characters needed. This is
# similar to how characters wrap on a terminal. This is the default
# split mechanism when #hard_margins is enabled.
#
# repre
# senta
# ion
SPLIT_FIXED = 1
# When #hard_margins is enabled, a word that extends over the right
# margin will be split at one less than the number of characters needed
# with a C-style continuation character (\). If the word cannot be split
# using the rules of SPLIT_CONTINUATION, and the word will not fit
# wholly into the next line, then SPLIT_FIXED will be used.
#
# repr\
# esen\
# tati\
# on
SPLIT_CONTINUATION = 2
# When #hard_margins is enabled, a word that extends over the right
# margin will be split according to the hyphenator specified by the
# #hyphenator object; if there is no hyphenation library supplied, then
# the hyphenator of Text::Format itself is used, which is the same as
# SPLIT_CONTINUATION. See #hyphenator for more information about
# hyphenation libraries. The example below is valid with either
# TeX::Hyphen or Text::Hyphen. If the word cannot be split using the
# hyphenator's rules, and the word will not fit wholly into the next
# line, then SPLIT_FIXED will be used.
#
# rep-
# re-
# sen-
# ta-
# tion
#
SPLIT_HYPHENATION = 4
# When #hard_margins is enabled, a word that extends over the right
# margin will be split at one less than the number of characters needed
# with a C-style continuation character (\). If the word cannot be split
# using the rules of SPLIT_CONTINUATION, then SPLIT_FIXED will be used.
SPLIT_CONTINUATION_FIXED = SPLIT_CONTINUATION | SPLIT_FIXED
# When #hard_margins is enabled, a word that extends over the right
# margin will be split according to the hyphenator specified by the
# #hyphenator object; if there is no hyphenation library supplied, then
# the hyphenator of Text::Format itself is used, which is the same as
# SPLIT_CONTINUATION. See #hyphenator for more information about
# hyphenation libraries. The example below is valid with either
# TeX::Hyphen or Text::Hyphen. If the word cannot be split using the
# hyphenator's rules, then SPLIT_FIXED will be used.
SPLIT_HYPHENATION_FIXED = SPLIT_HYPHENATION | SPLIT_FIXED
# Attempts to split words according to the rules of the supplied
# hyphenator (e.g., SPLIT_HYPHENATION); if the word cannot be split
# using these rules, then the rules of SPLIT_CONTINUATION will be
# followed. In all cases, if the word cannot be split using either
# SPLIT_HYPHENATION or SPLIT_CONTINUATION, and the word will not fit
# wholly into the next line, then SPLIT_FIXED will be used.
SPLIT_HYPHENATION_CONTINUATION = SPLIT_HYPHENATION | SPLIT_CONTINUATION
# Attempts to split words according to the rules of the supplied
# hyphenator (e.g., SPLIT_HYPHENATION); if the word cannot be split
# using these rules, then the rules of SPLIT_CONTINUATION will be
# followed. In all cases, if the word cannot be split using either
# SPLIT_HYPHENATION or SPLIT_CONTINUATION, then SPLIT_FIXED will be
# used.
SPLIT_ALL = SPLIT_HYPHENATION | SPLIT_CONTINUATION | SPLIT_FIXED
# Words forcibly split by Text::Format will be stored as split words.
# This class represents a word forcibly split.
class SplitWord
# The word that was split.
attr_reader :word
# The first part of the word that was split.
attr_reader :first
# The remainder of the word that was split.
attr_reader :rest
def initialize(word, first, rest)
@word = word
@first = first
@rest = rest
end
end
# Indicates punctuation characters that terminates a sentence, as some
# English typesetting rules indicate that sentences should be followed
# by two spaces. This is an archaic rule, but is supported with
# #extra_space. This is the default set of terminal punctuation
# characters. Additional terminal punctuation may be added to the
# formatting object through #terminal_punctuation.
TERMINAL_PUNCTUATION = %q(.?!)
# Indicates quote characters that may follow terminal punctuation under
# the current formatting rules. This satisfies the English formatting
# rule that indicates that sentences terminated inside of quotes should
# have the punctuation inside of the quoted text, not outside of the
# terminal quote. Additional terminal quotes may be added to the
# formatting object through #terminal_quotes. See TERMINAL_PUNCTUATION
# for more information.
TERMINAL_QUOTES = %q('")
# This method returns the regular expression used to detect the end of a
# sentence under the current definition of TERMINAL_PUNCTUATION,
# #terminal_punctuation, TERMINAL_QUOTES, and #terminal_quotes.
def __sentence_end_re
%r{[#{TERMINAL_PUNCTUATION}#{self.terminal_punctuation}][#{TERMINAL_QUOTES}#{self.terminal_quotes}]?$}
end
private :__sentence_end_re
# Returns a regular expression for a set of characters (at least one
# non-whitespace followed by at least one space) of the specified size
# followed by one or more of any character.
RE_BREAK_SIZE = lambda { |size| %r[((?:\S+\s+){#{size}})(.+)] }
# Compares the formatting rules, excepting #hyphenator, of two
# Text::Format objects. Generated results (e.g., #split_words) are not
# compared.
def ==(o)
(@text == o.text) and
(@columns == o.columns) and
(@left_margin == o.left_margin) and
(@right_margin == o.right_margin) and
(@hard_margins == o.hard_margins) and
(@split_rules == o.split_rules) and
(@first_indent == o.first_indent) and
(@body_indent == o.body_indent) and
(@tag_text == o.tag_text) and
(@tabstop == o.tabstop) and
(@format_style == o.format_style) and
(@extra_space == o.extra_space) and
(@tag_paragraph == o.tag_paragraph) and
(@nobreak == o.nobreak) and
(@terminal_punctuation == o.terminal_punctuation) and
(@terminal_quotes == o.terminal_quotes) and
(@abbreviations == o.abbreviations) and
(@nobreak_regex == o.nobreak_regex)
end
# The default text to be manipulated. Note that value is optional, but
# if the formatting functions are called without values, this text is
# what will be formatted.
#
# *Default*:: <tt>[]</tt>
# <b>Used in</b>:: All methods
attr_accessor :text
# The total width of the format area. The margins, indentation, and text
# are formatted into this space. Any value provided is silently
# converted to a positive integer.
#
# COLUMNS
# <-------------------------------------------------------------->
# <-----------><------><---------------------------><------------>
# left margin indent text is formatted into here right margin
#
# *Default*:: <tt>72</tt>
# <b>Used in</b>:: #format, #paragraphs, #center
attr_accessor :columns
def columns=(col) #:nodoc:
@columns = col.to_i.abs
end
# The number of spaces used for the left margin. The value provided is
# silently converted to a positive integer value.
#
# columns
# <-------------------------------------------------------------->
# <-----------><------><---------------------------><------------>
# LEFT MARGIN indent text is formatted into here right margin
#
# *Default*:: <tt>0</tt>
# <b>Used in</b>:: #format, #paragraphs, #center
attr_accessor :left_margin
def left_margin=(left) #:nodoc:
@left_margin = left.to_i.abs
end
# The number of spaces used for the right margin. The value provided is
# silently converted to a positive integer value.
#
# columns
# <-------------------------------------------------------------->
# <-----------><------><---------------------------><------------>
# left margin indent text is formatted into here RIGHT MARGIN
#
# *Default*:: <tt>0</tt>
# <b>Used in</b>:: #format, #paragraphs, #center
attr_accessor :right_margin
def right_margin=(right) #:nodoc:
@right_margin = right.to_i.abs
end
# The number of spaces to indent the first line of a paragraph. The
# value provided is silently converted to a positive integer value.
#
# columns
# <-------------------------------------------------------------->
# <-----------><------><---------------------------><------------>
# left margin INDENT text is formatted into here right margin
#
# *Default*:: <tt>4</tt>
# <b>Used in</b>:: #format, #paragraphs
attr_accessor :first_indent
def first_indent=(first) #:nodoc:
@first_indent = first.to_i.abs
end
# The number of spaces to indent all lines after the first line of a
# paragraph. The value provided is silently converted to a positive
# integer value.
#
# columns
# <-------------------------------------------------------------->
# <-----------><------><---------------------------><------------>
# left margin INDENT text is formatted into here right margin
#
# *Default*:: <tt>0</tt>
# <b>Used in</b>:: #format, #paragraphs
attr_accessor :body_indent
def body_indent=(body) #:nodoc:
@body_indent = body.to_i.abs
end
# Normally, words larger than the format area will be placed on a line
# by themselves. Setting this value to +true+ will force words larger
# than the format area to be split into one or more "words" each at most
# the size of the format area. The first line and the original word will
# be placed into #split_words. Note that this will cause the output to
# look *similar* to a #format_style of JUSTIFY. (Lines will be filled as
# much as possible.)
#
# *Default*:: +false+
# <b>Used in</b>:: #format, #paragraphs
attr_accessor :hard_margins
# An array of words split during formatting if #hard_margins is set to
# +true+.
# #split_words << Text::Format::SplitWord.new(word, first, rest)
attr_reader :split_words
# The object responsible for hyphenating. It must respond to
# #hyphenate_to(word, size) or #hyphenate_to(word, size, formatter) and
# return an array of the word split into two parts (e.g., <tt>[part1,
# part2]</tt>; if there is a hyphenation mark to be applied,
# responsibility belongs to the hyphenator object. The size is the
# MAXIMUM size permitted, including any hyphenation marks.
#
# If the #hyphenate_to method has an arity of 3, the current formatter
# (+self+) will be provided to the method. This allows the hyphenator to
# make decisions about the hyphenation based on the formatting rules.
#
# #hyphenate_to should return <tt>[nil, word]</tt> if the word cannot be
# hyphenated.
#
# *Default*:: +self+ (SPLIT_CONTINUATION)
# <b>Used in</b>:: #format, #paragraphs
attr_accessor :hyphenator
def hyphenator=(h) #:nodoc:
h ||= self
raise ArgumentError, "#{h.inspect} is not a valid hyphenator." unless h.respond_to?(:hyphenate_to)
arity = h.method(:hyphenate_to).arity
raise ArgumentError, "#{h.inspect} must have exactly two or three arguments." unless arity.between?(2, 3)
@hyphenator = h
@hyphenator_arity = arity
end
# Specifies the split mode; used only when #hard_margins is set to
# +true+. Allowable values are:
#
# * +SPLIT_FIXED+
# * +SPLIT_CONTINUATION+
# * +SPLIT_HYPHENATION+
# * +SPLIT_CONTINUATION_FIXED+
# * +SPLIT_HYPHENATION_FIXED+
# * +SPLIT_HYPHENATION_CONTINUATION+
# * +SPLIT_ALL+
#
# *Default*:: <tt>Text::Format::SPLIT_FIXED</tt>
# <b>Used in</b>:: #format, #paragraphs
attr_accessor :split_rules
def split_rules=(s) #:nodoc:
raise ArgumentError, "Invalid value provided for #split_rules." if ((s < SPLIT_FIXED) or (s > SPLIT_ALL))
@split_rules = s
end
# Indicates whether sentence terminators should be followed by a single
# space (+false+), or two spaces (+true+). See #abbreviations for more
# information.
#
# *Default*:: +false+
# <b>Used in</b>:: #format, #paragraphs
attr_accessor :extra_space
# Defines the current abbreviations as an array. This is only used if
# extra_space is turned on.
#
# If one is abbreviating "President" as "Pres." (abbreviations =
# ["Pres"]), then the results of formatting will be as illustrated in
# the table below:
#
# abbreviations
# extra_space | #include?("Pres") | not #include?("Pres")
# ------------+-------------------+----------------------
# true | Pres. Lincoln | Pres. Lincoln
# false | Pres. Lincoln | Pres. Lincoln
# ------------+-------------------+----------------------
# extra_space | #include?("Mrs") | not #include?("Mrs")
# true | Mrs. Lincoln | Mrs. Lincoln
# false | Mrs. Lincoln | Mrs. Lincoln
#
# Note that abbreviations should not have the terminal period as part of
# their definitions.
#
# This automatic abbreviation handling *will* cause some issues with
# uncommon sentence structures. The two sentences below will not be
# formatted correctly:
#
# You're in trouble now, Mr.
# Just wait until your father gets home.
#
# Under no circumstances (because Mr is a predefined abbreviation) will
# this ever be separated by two spaces.
#
# *Default*:: <tt>[]</tt>
# <b>Used in</b>:: #format, #paragraphs
attr_accessor :abbreviations
# Specifies additional punctuation characters that terminate a sentence,
# as some English typesetting rules indicate that sentences should be
# followed by two spaces. This is an archaic rule, but is supported with
# #extra_space. This is added to the default set of terminal punctuation
# defined in TERMINAL_PUNCTUATION.
#
# *Default*:: <tt>""</tt>
# <b>Used in</b>:: #format, #paragraphs
attr_accessor :terminal_punctuation
# Specifies additional quote characters that may follow
# terminal punctuation under the current formatting rules. This
# satisfies the English formatting rule that indicates that sentences
# terminated inside of quotes should have the punctuation inside of the
# quoted text, not outside of the terminal quote. This is added to the
# default set of terminal quotes defined in TERMINAL_QUOTES.
#
# *Default*:: <tt>""</tt>
# <b>Used in</b>:: #format, #paragraphs
attr_accessor :terminal_quotes
# Indicates whether the formatting of paragraphs should be done with
# tagged paragraphs. Useful only with #tag_text.
#
# *Default*:: +false+
# <b>Used in</b>:: #format, #paragraphs
attr_accessor :tag_paragraph
# The text to be placed before each paragraph when #tag_paragraph is
# +true+. When #format is called, only the first element (#tag_text[0])
# is used. When #paragraphs is called, then each successive element
# (#tag_text[n]) will be used once, with corresponding paragraphs. If
# the tag elements are exhausted before the text is exhausted, then the
# remaining paragraphs will not be tagged. Regardless of indentation
# settings, a blank line will be inserted between all paragraphs when
# #tag_paragraph is +true+.
#
# The Text::Format package provides three number generators,
# Text::Format::Alpha, Text::Format::Number, and Text::Format::Roman to
# assist with the numbering of paragraphs.
#
# *Default*:: <tt>[]</tt>
# <b>Used in</b>:: #format, #paragraphs
attr_accessor :tag_text
# Indicates whether or not the non-breaking space feature should be
# used.
#
# *Default*:: +false+
# <b>Used in</b>:: #format, #paragraphs
attr_accessor :nobreak
# A hash which holds the regular expressions on which spaces should not
# be broken. The hash is set up such that the key is the first word and
# the value is the second word.
#
# For example, if +nobreak_regex+ contains the following hash:
#
# { %r{Mrs?\.?} => %r{\S+}, %r{\S+} => %r{(?:[SJ])r\.?} }
#
# Then "Mr. Jones", "Mrs Jones", and "Jones Jr." would not be broken. If
# this simple matching algorithm indicates that there should not be a
# break at the current end of line, then a backtrack is done until there
# are two words on which line breaking is permitted. If two such words
# are not found, then the end of the line will be broken *regardless*.
# If there is a single word on the current line, then no backtrack is
# done and the word is stuck on the end.
#
# *Default*:: <tt>{}</tt>
# <b>Used in</b>:: #format, #paragraphs
attr_accessor :nobreak_regex
# Indicates the number of spaces that a single tab represents. Any value
# provided is silently converted to a positive integer.
#
# *Default*:: <tt>8</tt>
# <b>Used in</b>:: #expand, #unexpand,
# #paragraphs
attr_accessor :tabstop
def tabstop=(tabs) #:nodoc:
@tabstop = tabs.to_i.abs
end
# Specifies the format style. Allowable values are:
# *+LEFT_ALIGN+
# *+RIGHT_ALIGN+
# *+RIGHT_FILL+
# *+JUSTIFY+
#
# *Default*:: <tt>Text::Format::LEFT_ALIGN</tt>
# <b>Used in</b>:: #format, #paragraphs
attr_accessor :format_style
def format_style=(fs) #:nodoc:
raise ArgumentError, "Invalid value provided for format_style." unless [LEFT_ALIGN, RIGHT_ALIGN, RIGHT_FILL, JUSTIFY].include?(fs)
@format_style = fs
end
# Indicates that the format style is left alignment.
#
# *Default*:: +true+
# <b>Used in</b>:: #format, #paragraphs
def left_align?
@format_style == LEFT_ALIGN
end
# Indicates that the format style is right alignment.
#
# *Default*:: +false+
# <b>Used in</b>:: #format, #paragraphs
def right_align?
@format_style == RIGHT_ALIGN
end
# Indicates that the format style is right fill.
#
# *Default*:: +false+
# <b>Used in</b>:: #format, #paragraphs
def right_fill?
@format_style == RIGHT_FILL
end
# Indicates that the format style is full justification.
#
# *Default*:: +false+
# <b>Used in</b>:: #format, #paragraphs
def justify?
@format_style == JUSTIFY
end
# The formatting object itself can be used as a #hyphenator, where the
# default implementation of #hyphenate_to implements the conditions
# necessary to properly produce SPLIT_CONTINUATION.
def hyphenate_to(word, size)
if (size - 2) < 0
[nil, word]
else
[word[0 .. (size - 2)] + "\\", word[(size - 1) .. -1]]
end
end
# Splits the provided word so that it is in two parts, <tt>word[0 ..
# (size - 1)]</tt> and <tt>word[size .. -1]</tt>.
def split_word_to(word, size)
[word[0 .. (size - 1)], word[size .. -1]]
end
# Formats text into a nice paragraph format. The text is separated into
# words and then reassembled a word at a time using the settings of this
# Format object.
#
# If +text+ is +nil+, then the value of #text will be worked on.
def format_one_paragraph(text = nil)
text ||= @text
text = text[0] if text.kind_of?(Array)
# Convert the provided paragraph to a list of words.
words = text.split(SPACES_RE).reverse.reject { |ww| ww.nil? or ww.empty? }
text = []
# Find the maximum line width and the initial indent string.
# TODO 20050114 - allow the left and right margins to be specified as
# strings. If they are strings, then we need to use the sizes of the
# strings. Also: allow the indent string to be set manually and
# indicate whether the indent string will have a following space.
max_line_width = @columns - @first_indent - @left_margin - @right_margin
indent_str = ' ' * @first_indent
first_line = true
if words.empty?
line = []
line_size = 0
extra_space = false
else
line = [ words.pop ]
line_size = line[-1].size
extra_space = __add_extra_space?(line[-1])
end
while next_word = words.pop
next_word.strip! unless next_word.nil?
new_line_size = (next_word.size + line_size) + 1
if extra_space
if (line[-1] !~ __sentence_end_re)
extra_space = false
end
end
# Increase the width of the new line if there's a sentence
# terminator and we are applying extra_space.
new_line_size += 1 if extra_space
# Will the word fit onto the current line? If so, simply append it
# to the end of the line.
if new_line_size <= max_line_width
if line.empty?
line << next_word
else
if extra_space
line << " #{next_word}"
else
line << " #{next_word}"
end
end
else
# Forcibly wrap the line if nonbreaking spaces are turned on and
# there is a condition where words must be wrapped. If we have
# returned more than one word, readjust the word list.
line, next_word = __wrap_line(line, next_word) if @nobreak
if next_word.kind_of?(Array)
if next_word.size > 1
words.push(*(next_word.reverse))
next_word = words.pop
else
next_word = next_word[0]
end
next_word.strip! unless next_word.nil?
end
# Check to see if the line needs to be hyphenated. If a word has a
# hyphen in it (e.g., "fixed-width"), then we can ALWAYS wrap at
# that hyphenation, even if #hard_margins is not turned on. More
# elaborate forms of hyphenation will only be performed if
# #hard_margins is turned on. If we have returned more than one
# word, readjust the word list.
line, new_line_size, next_word = __hyphenate(line, line_size, next_word, max_line_width)
if next_word.kind_of?(Array)
if next_word.size > 1
words.push(*(next_word.reverse))
next_word = words.pop
else
next_word = next_word[0]
end
next_word.strip! unless next_word.nil?
end
text << __make_line(line, indent_str, max_line_width, next_word.nil?) unless line.nil?
if first_line
first_line = false
max_line_width = @columns - @body_indent - @left_margin - @right_margin
indent_str = ' ' * @body_indent
end
if next_word.nil?
line = []
new_line_size = 0
else
line = [ next_word ]
new_line_size = next_word.size
end
end
line_size = new_line_size
extra_space = __add_extra_space?(next_word) unless next_word.nil?
end
loop do
break if line.nil? or line.empty?
line, line_size, ww = __hyphenate(line, line_size, ww, max_line_width)#if @hard_margins
text << __make_line(line, indent_str, max_line_width, ww.nil?)
line = ww
ww = nil
end
if (@tag_paragraph and (not text.empty?))
if @tag_cur.nil? or @tag_cur.empty?
@tag_cur = @tag_text[0]
end
fchar = /(\S)/o.match(text[0])[1]
white = text[0].index(fchar)
unless @tag_cur.nil?
if ((white - @left_margin - 1) > @tag_cur.size) then
white = @tag_cur.size + @left_margin
text[0].gsub!(/^ {#{white}}/, "#{' ' * @left_margin}#{@tag_cur}")
else
text.unshift("#{' ' * @left_margin}#{@tag_cur}\n")
end
end
end
text.join('')
end
alias format format_one_paragraph
# Considers each element of text (provided or internal) as a paragraph.
# If #first_indent is the same as #body_indent, then paragraphs will be
# separated by a single empty line in the result; otherwise, the
# paragraphs will follow immediately after each other. Uses #format to
# do the heavy lifting.
#
# If +to_wrap+ responds to #split, then it will be split into an array
# of elements by calling #split with the value of +split_on+. The
# default value of split_on is $/, or the default record separator,
# repeated twice (e.g., /\n\n/).
def paragraphs(to_wrap = nil, split_on = /(#{$/}){2}/o)
to_wrap = @text if to_wrap.nil?
if to_wrap.respond_to?(:split)
to_wrap = to_wrap.split(split_on)
else
to_wrap = [to_wrap].flatten
end
if ((@first_indent == @body_indent) or @tag_paragraph) then
p_end = NEWLINE
else
p_end = ''
end
cnt = 0
ret = []
to_wrap.each do |tw|
@tag_cur = @tag_text[cnt] if @tag_paragraph
@tag_cur = '' if @tag_cur.nil?
line = format(tw)
ret << "#{line}#{p_end}" if (not line.nil?) and (line.size > 0)
cnt += 1
end
ret[-1].chomp! unless ret.empty?
ret.join('')
end
# Centers the text, preserving empty lines and tabs.
def center(to_center = nil)
to_center = @text if to_center.nil?
to_center = [to_center].flatten
tabs = 0
width = @columns - @left_margin - @right_margin
centered = []
to_center.each do |tc|
s = tc.strip
tabs = s.count(TAB)
tabs = 0 if tabs.nil?
ct = ((width - s.size - (tabs * @tabstop) + tabs) / 2)
ct = (width - @left_margin - @right_margin) - ct
centered << "#{s.rjust(ct)}\n"
end
centered.join('')
end
# Replaces all tab characters in the text with #tabstop spaces.
def expand(to_expand = nil)
to_expand = @text if to_expand.nil?
tmp = ' ' * @tabstop
changer = lambda do |text|
res = text.split(NEWLINE_RE)
res.collect! { |ln| ln.gsub!(/\t/o, tmp) }
res.join(NEWLINE)
end
if to_expand.kind_of?(Array)
to_expand.collect { |te| changer[te] }
else
changer[to_expand]
end
end
# Replaces all occurrences of #tabstop consecutive spaces with a tab
# character.
def unexpand(to_unexpand = nil)
to_unexpand = @text if to_unexpand.nil?
tmp = / {#{@tabstop}}/
changer = lambda do |text|
res = text.split(NEWLINE_RE)
res.collect! { |ln| ln.gsub!(tmp, TAB) }
res.join(NEWLINE)
end
if to_unexpand.kind_of?(Array)
to_unexpand.collect { |tu| changer[tu] }
else
changer[to_unexpand]
end
end
# Return +true+ if the word may have an extra space added after it. This
# will only be the case if #extra_space is +true+ and the word is not an
# abbreviation.
def __add_extra_space?(word)
return false unless @extra_space
word = word.gsub(/\.$/o, '') unless word.nil?
return false if ABBREV.include?(word)
return false if @abbreviations.include?(word)
true
end
private :__add_extra_space?
def __make_line(line, indent, width, last = false) #:nodoc:
line_size = line.inject(0) { |ls, el| ls + el.size }
lmargin = " " * @left_margin
fill = " " * (width - line_size) if right_fill? and (line_size <= width)
unless last
if justify? and (line.size > 1)
spaces = width - line_size
word_spaces = spaces / (line.size / 2)
spaces = spaces % (line.size / 2) if word_spaces > 0
line.reverse.each do |word|
next if (word =~ /^\S/o)
word.sub!(/^/o, " " * word_spaces)
next unless (spaces > 0)
word.sub!(/^/o, " ")
spaces -= 1
end
end
end
line = "#{lmargin}#{indent}#{line.join('')}#{fill}\n" unless line.empty?
if right_align? and (not line.nil?)
line.sub(/^/o, " " * (@columns - @right_margin - (line.size - 1)))
else
line
end
end
# private :__make_line
def __hyphenate(line, line_size, next_word, width) #:nodoc:
return [ line, line_size, next_word ] if line.nil? or line.empty?
rline = line.dup
rsize = line_size
rnext = []
rnext << next_word.dup unless next_word.nil?
loop do
break if rnext.nil? or rline.nil?
if rsize == width
break
elsif rsize > width
word = rline.pop
size = width - rsize + word.size
if (size < 1)
rnext.unshift word
next
end
first = rest = nil
# TODO: Add the check to see if the word contains a hyphen to
# split on automatically.
# Does the word already have a hyphen in it? If so, try to use
# that to split the word.
# if word.index('-') < size
# first = word[0 ... word.index("-")]
# rest = word[word.index("-") .. -1]
# end
if @hard_margins
if first.nil? and (@split_rules & SPLIT_HYPHENATION) == SPLIT_HYPHENATION
if @hyphenator_arity == 2
first, rest = @hyphenator.hyphenate_to(word, size)
else
first, rest = @hyphenator.hyphenate_to(word, size, self)
end
end
if first.nil? and (@split_rules & SPLIT_CONTINUATION) == SPLIT_CONTINUATION
first, rest = self.hyphenate_to(word, size)
end
if first.nil?
if (@split_rules & SPLIT_FIXED) == SPLIT_FIXED
first, rest = split_word_to(word, size)
elsif (not rest.nil? and (rest.size > size))
first, rest = split_word_to(word, size)
end
end
else
first = word if first.nil?
end
if first.nil?
rest = word
else
rsize = rsize - word.size + first.size
if rline.empty?
rline << first
else
rsize += 1
rline << " #{first}"
end
@split_words << SplitWord.new(word, first, rest)
end
rnext.unshift rest unless rest.nil?
break
else
break if rnext.empty?
word = rnext.shift.dup
size = width - rsize - 1
if (size <= 0)
rnext.unshift word
break
end
first = rest = nil
# TODO: Add the check to see if the word contains a hyphen to
# split on automatically.
# Does the word already have a hyphen in it? If so, try to use
# that to split the word.
# if word.index('-') < size
# first = word[0 ... word.index("-")]
# rest = word[word.index("-") .. -1]
# end
if @hard_margins
if (@split_rules & SPLIT_HYPHENATION) == SPLIT_HYPHENATION
if @hyphenator_arity == 2
first, rest = @hyphenator.hyphenate_to(word, size)
else
first, rest = @hyphenator.hyphenate_to(word, size, self)
end
end
if first.nil? and (@split_rules & SPLIT_CONTINUATION) == SPLIT_CONTINUATION
first, rest = self.hyphenate_to(word, size)
end
if first.nil?
if (@split_rules & SPLIT_FIXED) == SPLIT_FIXED
first, rest = split_word_to(word, size)
elsif (not rest.nil? and (rest.size > width))
first, rest = split_word_to(word, size)
end
end
else
first = word if first.nil?
end
# The word was successfully split. Does it fit?
unless first.nil?
if (rsize + first.size) < width
@split_words << SplitWord.new(word, first, rest)
rsize += first.size + 1
rline << " #{first}"
else
rest = word
end
else
rest = word unless rest.nil?
end
rnext.unshift rest
break
end
end
[ rline, rsize, rnext ]
end
private :__hyphenate
# The line must be broken. Typically, this is done by moving the last
# word on the current line to the next line. However, it may be possible
# that certain combinations of words may not be broken (see
# #nobreak_regex for more information). Therefore, it may be necessary
# to move multiple words from the current line to the next line. This
# function does this.
def __wrap_line(line, next_word)
no_break = false
word_index = line.size - 1
@nobreak_regex.each_pair do |first, second|
if line[word_index] =~ first and next_word =~ second
no_break = true
end
end
# If the last word and the next word aren't to be broken, and the line
# has more than one word in it, then we need to go back by words to
# ensure that we break as allowed.
if no_break and word_index.nonzero?
word_index -= 1
while word_index.nonzero?
no_break = false
@nobreak_regex.each_pair { |first, second|
if line[word_index] =~ first and line[word_index + 1] =~ second
no_break = true
end
}
break unless no_break
word_index -= 1
end
if word_index.nonzero?
words = line.slice!(word_index .. -1)
words << next_word
end
end
[line, words]
end
private :__wrap_line
# Create a Text::Format object. Accepts an optional hash of construction
# options (this will be changed to named paramters in Ruby 2.0). After
# the initial object is constructed (with either the provided or default
# values), the object will be yielded (as +self+) to an optional block
# for further construction and operation.
def initialize(options = {}) #:yields self:
@text = options[:text] || []
@columns = options[:columns] || 72
@tabstop = options[:tabstop] || 8
@first_indent = options[:first_indent] || 4
@body_indent = options[:body_indent] || 0
@format_style = options[:format_style] || LEFT_ALIGN
@left_margin = options[:left_margin] || 0
@right_margin = options[:right_margin] || 0
@extra_space = options[:extra_space] || false
@tag_paragraph = options[:tag_paragraph] || false
@tag_text = options[:tag_text] || []
@abbreviations = options[:abbreviations] || []
@terminal_punctuation = options[:terminal_punctuation] || ""
@terminal_quotes = options[:terminal_quotes] || ""
@nobreak = options[:nobreak] || false
@nobreak_regex = options[:nobreak_regex] || {}
@hard_margins = options[:hard_margins] || false
@split_rules = options[:split_rules] || SPLIT_FIXED
@hyphenator = options[:hyphenator] || self
@hyphenator_arity = @hyphenator.method(:hyphenate_to).arity
@tag_cur = ""
@split_words = []
yield self if block_given?
end
end
|