File: text_flowed.rb

package info (click to toggle)
ruby-mime 0.4.4-2
  • links: PTS
  • area: main
  • in suites: bookworm, bullseye, buster, forky, sid, trixie
  • size: 284 kB
  • sloc: ruby: 1,187; xml: 17; makefile: 2
file content (126 lines) | stat: -rw-r--r-- 3,844 bytes parent folder | download | duplicates (3)
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
#
# == Content Formats
#
# [Text/Plain Flowed] TextFlowed
# [Quoted-Printable]  To be implemented ...
# [Base64]            To be implemented ...
#
module MIME::ContentFormats

  #
  # Minimal implementation of RFC 2646: The Text/Plain Format Parameter
  #
  # https://tools.ietf.org/html/rfc2646
  #
  # == Excerpts from RFC
  #
  # This memo proposes a new parameter to be used with Text/Plain, and, in the
  # presence of this parameter, the use of trailing whitespace to indicate
  # flowed lines.  This results in an encoding which appears as normal
  # Text/Plain in older implementations, since it is in fact normal
  # Text/Plain.
  #
  # Each paragraph is displayed, starting at the left margin (or paragraph
  # indent), and continuing to the right until a word is encountered which
  # does not fit in the remaining display width. This word is displayed at the
  # left margin of the next line. This continues until the paragraph ends (a
  # CRLF is seen).
  #
  # == MIME format parameter to the text/plain media type
  #
  #   Name:  Format
  #   Value: Fixed, Flowed
  #   Example: Content-Type: text/plain; charset=iso-8859-1; format=flowed
  #
  #--
  # == TODO
  # - Implement RFC 3676, which obsoletes RFC 2646.
  # - Usenet signature convention (section 4.3)
  # - Space-Stuffing (section 4.4)
  # - Quoting (section 4.5)
  # - Perhaps this should be subsumed into the MIME project.
  #
  module TextFlowed

    MAX_FLOWED_LINE = 79

    #
    # Encode plain +text+ into flowed format, reducing long lines to +max+
    # characters or less using soft line breaks (i.e., SPACE+CRLF).
    #
    # According to the RFC, the +max+ flowed line length is 79 characters. Line
    # lengths of 66 and 72 characters are common.
    #
    # The features of RFC 2646, such as line quoting and space-stuffing,
    # are not implemented.
    #
    def self.encode(text, max = MAX_FLOWED_LINE)
      if max > MAX_FLOWED_LINE
        raise ArgumentError, "flowed lines must be #{MAX_FLOWED_LINE} characters or less"
      end

      out = []
      text.split(/\r\n|\n/).each do |paragraph|
        # tab use is discouraged
        # http://tools.ietf.org/html/rfc822#section-3.4.2 
        paragraph.gsub!(/\t/, ' '*4)

        # trim spaces before hard break
        # http://tools.ietf.org/html/rfc2646#section-4.1
        paragraph.rstrip!

        if paragraph.length <= max
          out << paragraph
        else # flow text
          line = ''
          word = ''

          paragraph.each_char do |char|
            if char == ' '
              # Omit spaces after soft break to prevent stuffing on next line.
              next if word.empty? && (line.size == 0 || line.size == max)

              if (line.size + word.size) < max
                line << word + char
              else # soft break situation
                unless line.empty?
                  out << line.dup
                  line.clear
                end
                if word.size < max
                  line << word + char
                else
                  word.scan(/.{1,#{MIME::MAX_LINE_LENGTH}}/) {|s| out << s }
                end
              end
              word.clear
            else  # accumulate non-space characters in buffer
              word += char
            end
          end

          # flush buffers in an orderly fashion
          if ! word.empty?
            if (line.size + word.size) <= max
              out << line + word
            else
              out << line unless line.empty?
              word.scan(/.{1,#{MIME::MAX_LINE_LENGTH}}/) {|s| out << s }
            end
          elsif ! line.empty?
            out << line
          end
        end
      end

      out.join("\r\n")
    end

    #
    # Decode flowed plain +text+.
    #
    def self.decode(text)
      raise NotImplementedError
    end
  end
end