rrtf 0.1.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.byebug_history +6 -3
- data/CHANGELOG.md +24 -0
- data/README.md +194 -84
- data/documentation/RRTF.html +5 -5
- data/documentation/RRTF/AnonymousStyle.html +792 -0
- data/documentation/RRTF/BorderFormatting.html +821 -0
- data/documentation/RRTF/BorderStyle.html +493 -0
- data/documentation/RRTF/CharacterFormatting.html +293 -162
- data/documentation/RRTF/CharacterStyle.html +53 -109
- data/documentation/RRTF/Colour.html +61 -1
- data/documentation/RRTF/ColourTable.html +52 -52
- data/documentation/RRTF/CommandNode.html +367 -971
- data/documentation/RRTF/ContainerNode.html +44 -44
- data/documentation/RRTF/Converters.html +1 -1
- data/documentation/RRTF/Converters/HTML.html +1 -1
- data/documentation/RRTF/Converters/HTML/Helpers.html +1 -1
- data/documentation/RRTF/Converters/HTML/Node.html +1 -1
- data/documentation/RRTF/Converters/HTML/NodeSet.html +1 -1
- data/documentation/RRTF/Document.html +267 -255
- data/documentation/RRTF/DocumentFormatting.html +833 -0
- data/documentation/RRTF/DocumentProperties.html +444 -0
- data/documentation/RRTF/Font.html +1 -1
- data/documentation/RRTF/FontTable.html +1 -1
- data/documentation/RRTF/FooterNode.html +16 -16
- data/documentation/RRTF/GeometryNode.html +774 -0
- data/documentation/RRTF/GeometryProperties.html +1014 -0
- data/documentation/RRTF/HeaderNode.html +16 -16
- data/documentation/RRTF/ImageNode.html +705 -492
- data/documentation/RRTF/Information.html +1 -1
- data/documentation/RRTF/LinkNode.html +10 -10
- data/documentation/RRTF/ListLevel.html +1 -1
- data/documentation/RRTF/ListLevelNode.html +37 -37
- data/documentation/RRTF/ListMarker.html +1 -1
- data/documentation/RRTF/ListNode.html +19 -19
- data/documentation/RRTF/ListTable.html +1 -1
- data/documentation/RRTF/ListTemplate.html +1 -1
- data/documentation/RRTF/ListTextNode.html +14 -14
- data/documentation/RRTF/Node.html +26 -26
- data/documentation/RRTF/Page.html +129 -0
- data/documentation/RRTF/Page/Margin.html +1158 -0
- data/documentation/RRTF/Page/Size.html +946 -0
- data/documentation/RRTF/PageFormatting.html +954 -0
- data/documentation/RRTF/ParagraphFormatting.html +338 -56
- data/documentation/RRTF/ParagraphNode.html +10 -10
- data/documentation/RRTF/ParagraphStyle.html +72 -111
- data/documentation/RRTF/PositionFormatting.html +780 -0
- data/documentation/RRTF/PositionStyle.html +424 -0
- data/documentation/RRTF/Properties.html +243 -0
- data/documentation/RRTF/RTFError.html +21 -10
- data/documentation/RRTF/ShadingFormatting.html +712 -0
- data/documentation/RRTF/ShadingStyle.html +424 -0
- data/documentation/RRTF/Style.html +284 -697
- data/documentation/RRTF/Stylesheet.html +36 -3
- data/documentation/RRTF/TableCellNode.html +131 -131
- data/documentation/RRTF/TableNode.html +82 -82
- data/documentation/RRTF/TableRowNode.html +53 -53
- data/documentation/RRTF/TextNode.html +46 -46
- data/documentation/RRTF/Utilities.html +837 -17
- data/documentation/_index.html +139 -6
- data/documentation/class_list.html +1 -1
- data/documentation/file.README.html +218 -87
- data/documentation/index.html +218 -87
- data/documentation/method_list.html +631 -391
- data/documentation/top-level-namespace.html +1 -1
- data/examples/01.rtf +947 -20
- data/examples/01_everything.rb +176 -0
- data/examples/02.rtf +13 -0
- data/examples/02_basic_paragraph.rb +10 -0
- data/examples/03.rtf +20 -0
- data/examples/03_paragraph_inline_style.rb +14 -0
- data/examples/04.rtf +21 -0
- data/examples/04_paragraph_with_character_style.rb +18 -0
- data/examples/05.rtf +21 -0
- data/examples/05_hyperlinks.rb +21 -0
- data/examples/06.rtf +21 -0
- data/examples/06_basic_list.rb +21 -0
- data/examples/07.rtf +28 -0
- data/examples/07_nested_list.rb +27 -0
- data/examples/08.rtf +807 -0
- data/examples/08_images.rb +17 -0
- data/examples/09.rtf +84 -0
- data/examples/09_shapes.rb +56 -0
- data/examples/10.rtf +34 -0
- data/examples/10_stylesheet.rb +18 -0
- data/examples/resources/images/redshirt.png +0 -0
- data/examples/resources/images/redshirts.jpg +0 -0
- data/examples/resources/json/redshirt_styles.json +72 -8
- data/examples/~$01.rtf +0 -0
- data/lib/rrtf.rb +4 -16
- data/lib/rrtf/colour.rb +8 -0
- data/lib/rrtf/formatting.rb +988 -0
- data/lib/rrtf/node.rb +17 -1851
- data/lib/rrtf/node/command_node.rb +242 -0
- data/lib/rrtf/node/container_node.rb +75 -0
- data/lib/rrtf/node/document.rb +339 -0
- data/lib/rrtf/node/footer_node.rb +47 -0
- data/lib/rrtf/node/geometry_node.rb +65 -0
- data/lib/rrtf/node/header_node.rb +47 -0
- data/lib/rrtf/node/image_node.rb +175 -0
- data/lib/rrtf/node/link_node.rb +10 -0
- data/lib/rrtf/node/list_level_node.rb +44 -0
- data/lib/rrtf/node/list_node.rb +30 -0
- data/lib/rrtf/node/list_text_node.rb +22 -0
- data/lib/rrtf/node/node.rb +53 -0
- data/lib/rrtf/node/paragraph_node.rb +11 -0
- data/lib/rrtf/node/table_cell_node.rb +233 -0
- data/lib/rrtf/node/table_node.rb +136 -0
- data/lib/rrtf/node/table_row_node.rb +92 -0
- data/lib/rrtf/node/text_node.rb +76 -0
- data/lib/rrtf/page.rb +7 -0
- data/lib/rrtf/page/margin.rb +98 -0
- data/lib/rrtf/page/size.rb +98 -0
- data/lib/rrtf/properties.rb +3 -0
- data/lib/rrtf/properties/document_properties.rb +34 -0
- data/lib/rrtf/properties/geometry_properties.rb +380 -0
- data/lib/rrtf/properties/properties.rb +13 -0
- data/lib/rrtf/style.rb +4 -5
- data/lib/rrtf/style/anonymous_style.rb +73 -0
- data/lib/rrtf/style/border_style.rb +27 -0
- data/lib/rrtf/style/character_style.rb +1 -7
- data/lib/rrtf/style/paragraph_style.rb +0 -6
- data/lib/rrtf/style/position_style.rb +26 -0
- data/lib/rrtf/style/shading_style.rb +26 -0
- data/lib/rrtf/style/style.rb +60 -101
- data/lib/rrtf/utilities.rb +138 -0
- data/lib/rrtf/version.rb +1 -1
- data/rrtf.gemspec +1 -0
- metadata +85 -10
- data/examples/01_mac_libreoffice5_2_3_3.png +0 -0
- data/examples/01_mac_pages6_2.png +0 -0
- data/examples/01_mac_textedit1_12.png +0 -0
- data/examples/01_mac_word15_36.png +0 -0
- data/examples/01_styles_and_paragraphs.rb +0 -32
- data/lib/rrtf/paper.rb +0 -53
- data/lib/rrtf/style/document_style.rb +0 -116
- data/lib/rrtf/style/formatting.rb +0 -320
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module RRTF
|
|
2
|
+
# This class represents a document footer.
|
|
3
|
+
class FooterNode < CommandNode
|
|
4
|
+
# A definition for a header type.
|
|
5
|
+
UNIVERSAL = :footer
|
|
6
|
+
|
|
7
|
+
# A definition for a header type.
|
|
8
|
+
LEFT_PAGE = :footerl
|
|
9
|
+
|
|
10
|
+
# A definition for a header type.
|
|
11
|
+
RIGHT_PAGE = :footerr
|
|
12
|
+
|
|
13
|
+
# A definition for a header type.
|
|
14
|
+
FIRST_PAGE = :footerf
|
|
15
|
+
|
|
16
|
+
# Attribute accessor.
|
|
17
|
+
attr_reader :type
|
|
18
|
+
|
|
19
|
+
# Attribute mutator.
|
|
20
|
+
attr_writer :type
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# This is the constructor for the FooterNode class.
|
|
24
|
+
#
|
|
25
|
+
# ==== Parameters
|
|
26
|
+
# document:: A reference to the Document object that will own the new
|
|
27
|
+
# footer.
|
|
28
|
+
# type:: The style type for the new footer. Defaults to a value of
|
|
29
|
+
# FooterNode::UNIVERSAL.
|
|
30
|
+
def initialize(document, type=UNIVERSAL)
|
|
31
|
+
super(document, "\\#{type.id2name}", nil, false)
|
|
32
|
+
@type = type
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# This method overloads the footnote method inherited from the CommandNode
|
|
36
|
+
# class to prevent footnotes being added to footers.
|
|
37
|
+
#
|
|
38
|
+
# ==== Parameters
|
|
39
|
+
# text:: Not used.
|
|
40
|
+
#
|
|
41
|
+
# ==== Exceptions
|
|
42
|
+
# RTFError:: Always generated whenever this method is called.
|
|
43
|
+
def footnote(text)
|
|
44
|
+
RTFError.fire("Footnotes are not permitted in page footers.")
|
|
45
|
+
end
|
|
46
|
+
end # End of the FooterNode class.
|
|
47
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module RRTF
|
|
2
|
+
# This class represents a geometry object (shape or text box) within an RTF
|
|
3
|
+
# document.
|
|
4
|
+
# @author Wesley Hileman
|
|
5
|
+
# @since 1.0.0
|
|
6
|
+
class GeometryNode < CommandNode
|
|
7
|
+
|
|
8
|
+
attr_reader :properties
|
|
9
|
+
|
|
10
|
+
# Constructor for the GeometryNode class.
|
|
11
|
+
#
|
|
12
|
+
# @param [Hash, GeometryProperties] properties a hash or GeometryProperties
|
|
13
|
+
# object specifying the properties of the geometry object.
|
|
14
|
+
def initialize(parent, properties = nil)
|
|
15
|
+
case properties
|
|
16
|
+
when Hash
|
|
17
|
+
@properties = GeometryProperties.new(properties)
|
|
18
|
+
when GeometryProperties
|
|
19
|
+
@properties = properties
|
|
20
|
+
else
|
|
21
|
+
RTFError.fire("Invalid geometry properties '#{properties}'.")
|
|
22
|
+
end unless properties.nil?
|
|
23
|
+
|
|
24
|
+
prefix = '{\shp{\*\shpinst'
|
|
25
|
+
prefix << @properties.to_rtf unless properties.nil?
|
|
26
|
+
|
|
27
|
+
super(parent, prefix, '}}', false, false)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_rtf
|
|
31
|
+
text = StringIO.new
|
|
32
|
+
|
|
33
|
+
text << @prefix
|
|
34
|
+
|
|
35
|
+
unless self.size() == 0
|
|
36
|
+
text << '{\shptxt'
|
|
37
|
+
self.each do |entry|
|
|
38
|
+
text << "\n"
|
|
39
|
+
text << entry.to_rtf
|
|
40
|
+
end # each
|
|
41
|
+
text << '}'
|
|
42
|
+
end # unless
|
|
43
|
+
|
|
44
|
+
text << @suffix
|
|
45
|
+
|
|
46
|
+
text.string
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Overrides the {CommandNode#geometry} method to prevent geometry objects
|
|
50
|
+
# from being nested in other geometry objects.
|
|
51
|
+
#
|
|
52
|
+
# @raise [RTFError] whenever called.
|
|
53
|
+
def geometry(properties = nil)
|
|
54
|
+
RTFError.fire("Cannot place a geometry object inside of another.")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Overrides the {CommandNode#<<} method to prevent text from being added
|
|
58
|
+
# to geometry objects directly. Calls {#paragraph} instead.
|
|
59
|
+
#
|
|
60
|
+
# @raise [RTFError] whenever called.
|
|
61
|
+
def <<(text)
|
|
62
|
+
self.paragraph << text
|
|
63
|
+
end
|
|
64
|
+
end # class GeometryNode
|
|
65
|
+
end # module RRTF
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module RRTF
|
|
2
|
+
# This class represents a document header.
|
|
3
|
+
class HeaderNode < CommandNode
|
|
4
|
+
# A definition for a header type.
|
|
5
|
+
UNIVERSAL = :header
|
|
6
|
+
|
|
7
|
+
# A definition for a header type.
|
|
8
|
+
LEFT_PAGE = :headerl
|
|
9
|
+
|
|
10
|
+
# A definition for a header type.
|
|
11
|
+
RIGHT_PAGE = :headerr
|
|
12
|
+
|
|
13
|
+
# A definition for a header type.
|
|
14
|
+
FIRST_PAGE = :headerf
|
|
15
|
+
|
|
16
|
+
# Attribute accessor.
|
|
17
|
+
attr_reader :type
|
|
18
|
+
|
|
19
|
+
# Attribute mutator.
|
|
20
|
+
attr_writer :type
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# This is the constructor for the HeaderNode class.
|
|
24
|
+
#
|
|
25
|
+
# ==== Parameters
|
|
26
|
+
# document:: A reference to the Document object that will own the new
|
|
27
|
+
# header.
|
|
28
|
+
# type:: The style type for the new header. Defaults to a value of
|
|
29
|
+
# HeaderNode::UNIVERSAL.
|
|
30
|
+
def initialize(document, type=UNIVERSAL)
|
|
31
|
+
super(document, "\\#{type.id2name}", nil, false)
|
|
32
|
+
@type = type
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# This method overloads the footnote method inherited from the CommandNode
|
|
36
|
+
# class to prevent footnotes being added to headers.
|
|
37
|
+
#
|
|
38
|
+
# ==== Parameters
|
|
39
|
+
# text:: Not used.
|
|
40
|
+
#
|
|
41
|
+
# ==== Exceptions
|
|
42
|
+
# RTFError:: Always generated whenever this method is called.
|
|
43
|
+
def footnote(text)
|
|
44
|
+
RTFError.fire("Footnotes are not permitted in page headers.")
|
|
45
|
+
end
|
|
46
|
+
end # End of the HeaderNode class.
|
|
47
|
+
end
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
require 'fastimage'
|
|
2
|
+
|
|
3
|
+
module RRTF
|
|
4
|
+
# This class represents an image within a RTF document. Currently only the
|
|
5
|
+
# PNG, JPEG and Windows Bitmap formats are supported. Efforts are made to
|
|
6
|
+
# identify the file type but these are not guaranteed to work.
|
|
7
|
+
# @author Peter Wood
|
|
8
|
+
# @author Wesley Hileman
|
|
9
|
+
# @since legacy
|
|
10
|
+
class ImageNode < Node
|
|
11
|
+
# Supported image types.
|
|
12
|
+
TYPE_DICTIONARY = {
|
|
13
|
+
:png => 'pngblip',
|
|
14
|
+
:jpeg => 'jpegblip',
|
|
15
|
+
:bmp => 'dibitmap0' # device independent bitmap
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
# Supported sizing modes.
|
|
19
|
+
SIZING_MODE_DICTIONARY = {
|
|
20
|
+
# Size the image absolutely according to the given width and height.
|
|
21
|
+
"ABSOLUTE" => "ABSOLUTE",
|
|
22
|
+
# Fit the image in the box specified by the given width and height,
|
|
23
|
+
# preserving the aspect ratio.
|
|
24
|
+
"FIX_ASPECT_RATIO" => "FIX_ASPECT_RATIO"
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
attr_reader :type, :width, :height, :displayed_width, :displayed_height, :sizing_mode, :border
|
|
28
|
+
attr_writer :displayed_width, :displayed_height, :sizing_mode, :border
|
|
29
|
+
|
|
30
|
+
# Attempts to extract the type, width, and height of an image using
|
|
31
|
+
# FastImage.
|
|
32
|
+
#
|
|
33
|
+
# @param [String] source the file path to the source image.
|
|
34
|
+
# @return [Array<Object>] a 3-tuple containing the type, width, and height
|
|
35
|
+
# of the image, respectively (type is a symbol, dimensions in pixels).
|
|
36
|
+
def self.inspect(source)
|
|
37
|
+
type, width, height = nil
|
|
38
|
+
|
|
39
|
+
type = TYPE_DICTIONARY[FastImage.type(source)]
|
|
40
|
+
width, height = FastImage.size(source) unless type.nil?
|
|
41
|
+
|
|
42
|
+
[type, width, height]
|
|
43
|
+
end # inspect()
|
|
44
|
+
|
|
45
|
+
def self.parse_border_array(border)
|
|
46
|
+
case border
|
|
47
|
+
when nil
|
|
48
|
+
[]
|
|
49
|
+
when BorderStyle
|
|
50
|
+
[border]
|
|
51
|
+
when Hash
|
|
52
|
+
[BorderStyle.new(border)]
|
|
53
|
+
when Array
|
|
54
|
+
border.collect{ |b| parse_border_array(b) }.flatten.compact
|
|
55
|
+
else
|
|
56
|
+
RTFError.fire("Invalid border #{b}.")
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# This is the constructor for the ImageNode class.
|
|
61
|
+
#
|
|
62
|
+
# @param parent [Node] a reference to the node that owns the new image node.
|
|
63
|
+
# @param source [String, File] a reference to the image source; this must be a String or a File.
|
|
64
|
+
# @param id [Integer] a unique 32-bit identifier for the image.
|
|
65
|
+
# @param options [Hash] a hash of options.
|
|
66
|
+
# @option options [String] "width" (nil) the display width of the image in twips (can be a string, see {Utilities.value2twips}).
|
|
67
|
+
# @option options [String] "height" (nil) the display height of the image in twips (can be a string, see {Utilities.value2twips}).
|
|
68
|
+
# @option options [String] "sizing_mode" ("ABSOLUTE") the method used to size the image (see {SIZING_MODE_DICTIONARY}).
|
|
69
|
+
# @raise [RTFError] whenever the image specified is not recognised as
|
|
70
|
+
# a supported image type, something other than a String or
|
|
71
|
+
# File or IO is passed as the source parameter or if the
|
|
72
|
+
# specified source does not exist or cannot be accessed.
|
|
73
|
+
def initialize(parent, source, id, options = {})
|
|
74
|
+
super(parent)
|
|
75
|
+
@source = nil
|
|
76
|
+
@id = id
|
|
77
|
+
|
|
78
|
+
# load default options
|
|
79
|
+
options = {
|
|
80
|
+
"width" => nil,
|
|
81
|
+
"height" => nil,
|
|
82
|
+
"sizing_mode" => "ABSOLUTE",
|
|
83
|
+
"border" => nil
|
|
84
|
+
}.merge(options)
|
|
85
|
+
|
|
86
|
+
# extract options
|
|
87
|
+
@displayed_width = Utilities.value2twips(options.delete("width"))
|
|
88
|
+
@displayed_height = Utilities.value2twips(options.delete("height"))
|
|
89
|
+
@sizing_mode = SIZING_MODE_DICTIONARY[options.delete("sizing_mode")]
|
|
90
|
+
@border = self.class.parse_border_array(options.delete("border"))
|
|
91
|
+
|
|
92
|
+
# store border colours in colour table
|
|
93
|
+
@border.each do |b|
|
|
94
|
+
b.push_colours(root.colours)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Store path to image.
|
|
98
|
+
@source = source if source.instance_of?(String) || source.instance_of?(Tempfile)
|
|
99
|
+
@source = source.path if source.instance_of?(File)
|
|
100
|
+
|
|
101
|
+
# Check the file's existence and accessibility.
|
|
102
|
+
if !File.exist?(@source)
|
|
103
|
+
RTFError.fire("Unable to find the #{File.basename(@source)} file.")
|
|
104
|
+
end
|
|
105
|
+
if !File.readable?(@source)
|
|
106
|
+
RTFError.fire("Access to the #{File.basename(@source)} file denied.")
|
|
107
|
+
end
|
|
108
|
+
# Attempt to determine image type and dimensions.
|
|
109
|
+
@type, @width, @height = self.class.inspect(@source)
|
|
110
|
+
if @type.nil?
|
|
111
|
+
RTFError.fire("The #{File.basename(@source)} file contains an unknown or unsupported image type.")
|
|
112
|
+
elsif @width.nil? || @height.nil?
|
|
113
|
+
RTFError.fire("Could not determine the dimensions of #{File.basename(@source)}.")
|
|
114
|
+
end # if
|
|
115
|
+
|
|
116
|
+
@displayed_width, @displayed_height = size_image
|
|
117
|
+
end # initialize()
|
|
118
|
+
|
|
119
|
+
# This method generates the RTF for an ImageNode object.
|
|
120
|
+
def to_rtf
|
|
121
|
+
text = StringIO.new
|
|
122
|
+
count = 0
|
|
123
|
+
|
|
124
|
+
text << '{\pict'
|
|
125
|
+
@border.each{ |b| text << " #{b.prefix(self.root)}" }
|
|
126
|
+
text << "\\picwgoal#{@displayed_width}" if @displayed_width != nil
|
|
127
|
+
text << "\\pichgoal#{@displayed_height}" if @displayed_height != nil
|
|
128
|
+
text << "\\picw#{@width}\\pich#{@height}\\bliptag#{@id}"
|
|
129
|
+
text << "\\#{@type}\n"
|
|
130
|
+
|
|
131
|
+
open_file do |file|
|
|
132
|
+
file.each_byte do |byte|
|
|
133
|
+
hex_str = byte.to_s(16)
|
|
134
|
+
hex_str.insert(0,'0') if hex_str.length == 1
|
|
135
|
+
text << hex_str
|
|
136
|
+
count += 1
|
|
137
|
+
if count == 40
|
|
138
|
+
text << "\n"
|
|
139
|
+
count = 0
|
|
140
|
+
end # if
|
|
141
|
+
end # each_byte
|
|
142
|
+
end # open_file
|
|
143
|
+
text << "\n}"
|
|
144
|
+
|
|
145
|
+
text.string
|
|
146
|
+
end # to_rtf()
|
|
147
|
+
|
|
148
|
+
private
|
|
149
|
+
|
|
150
|
+
def size_image
|
|
151
|
+
case @sizing_mode
|
|
152
|
+
when 'ABSOLUTE'
|
|
153
|
+
[@displayed_width, @displayed_height]
|
|
154
|
+
when 'FIX_ASPECT_RATIO'
|
|
155
|
+
width_ratio = @displayed_width ? (@displayed_width.to_f / @width.to_f) : nil
|
|
156
|
+
height_ratio = @displayed_height ? (@displayed_height.to_f / @height.to_f) : nil
|
|
157
|
+
scale_factor = [width_ratio, height_ratio].compact.min
|
|
158
|
+
|
|
159
|
+
if scale_factor.nil?
|
|
160
|
+
[@displayed_width, @displayed_height]
|
|
161
|
+
else
|
|
162
|
+
[(@width*scale_factor).to_i, (@height*scale_factor).to_i]
|
|
163
|
+
end
|
|
164
|
+
end # case
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def open_file(&block)
|
|
168
|
+
if block
|
|
169
|
+
File.open(@source, 'rb', &block)
|
|
170
|
+
else
|
|
171
|
+
File.open(@source, 'rb')
|
|
172
|
+
end # if
|
|
173
|
+
end # open_file()
|
|
174
|
+
end # End of the ImageNode class.
|
|
175
|
+
end # module RRTF
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module RRTF
|
|
2
|
+
# This class represents a list level, and carries out indenting information
|
|
3
|
+
# and the bullet or number that is prepended to each +ListTextNode+.
|
|
4
|
+
#
|
|
5
|
+
# The class overrides the +list+ method to implement nesting, and provides
|
|
6
|
+
# the +item+ method to add a new list item, the +ListTextNode+.
|
|
7
|
+
class ListLevelNode < CommandNode
|
|
8
|
+
def initialize(parent, template, kind, level=1)
|
|
9
|
+
@template = template
|
|
10
|
+
@kind = kind
|
|
11
|
+
@level = template.level_for(level, kind)
|
|
12
|
+
|
|
13
|
+
prefix = '\pard'
|
|
14
|
+
prefix << @level.tabs.map {|tw| "\\tx#{tw}"}.join
|
|
15
|
+
prefix << "\\li#{@level.indent}\\fi-#{@level.indent}"
|
|
16
|
+
prefix << "\\ql\\qlnatural\\pardirnatural\n"
|
|
17
|
+
prefix << "\\ls#{@template.id}\\ilvl#{@level.level-1}\\cf0"
|
|
18
|
+
|
|
19
|
+
super(parent, prefix, nil, true, false)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Returns the kind of this level, either :bullets or :decimal
|
|
23
|
+
attr_reader :kind
|
|
24
|
+
|
|
25
|
+
# Returns the indenting level of this list, from 1 to 9
|
|
26
|
+
def level
|
|
27
|
+
@level.level
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Creates a new +ListTextNode+ and yields it to the calling block
|
|
31
|
+
def item
|
|
32
|
+
node = ListTextNode.new(self, @level)
|
|
33
|
+
yield node
|
|
34
|
+
self.store(node)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Creates a new +ListLevelNode+ to implement nested lists
|
|
38
|
+
def list(kind=@kind)
|
|
39
|
+
node = ListLevelNode.new(self, @template, kind, @level.level+1)
|
|
40
|
+
yield node
|
|
41
|
+
self.store(node)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module RRTF
|
|
2
|
+
# This class represents an ordered/unordered list within an RTF document.
|
|
3
|
+
#
|
|
4
|
+
# Currently list nodes can contain any type of node, but this behaviour
|
|
5
|
+
# will change in future releases. The class overrides the +list+ method
|
|
6
|
+
# to return a +ListLevelNode+.
|
|
7
|
+
#
|
|
8
|
+
class ListNode < CommandNode
|
|
9
|
+
def initialize(parent)
|
|
10
|
+
prefix = "\\"
|
|
11
|
+
|
|
12
|
+
suffix = '\pard'
|
|
13
|
+
suffix << ListLevel::ResetTabs.map {|tw| "\\tx#{tw}"}.join
|
|
14
|
+
suffix << '\ql\qlnatural\pardirnatural\cf0 \\'
|
|
15
|
+
|
|
16
|
+
super(parent, prefix, suffix, true, false)
|
|
17
|
+
|
|
18
|
+
@template = root.lists.new_template
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# This method creates a new +ListLevelNode+ of the given kind and
|
|
22
|
+
# stores it in the document tree.
|
|
23
|
+
#
|
|
24
|
+
# ==== Parameters
|
|
25
|
+
# kind:: The kind of this list level, may be either :bullets or :decimal
|
|
26
|
+
def list(kind)
|
|
27
|
+
self.store ListLevelNode.new(self, @template, kind)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module RRTF
|
|
2
|
+
# This class represents a list item, that can contain text or
|
|
3
|
+
# other nodes. Currently any type of node is accepted, but after
|
|
4
|
+
# more extensive testing this behaviour may change.
|
|
5
|
+
class ListTextNode < CommandNode
|
|
6
|
+
def initialize(parent, level)
|
|
7
|
+
@level = level
|
|
8
|
+
@parent = parent
|
|
9
|
+
|
|
10
|
+
number = siblings_count + 1 if parent.kind == :decimal
|
|
11
|
+
prefix = "{\\listtext#{@level.marker.text_format(number)}}"
|
|
12
|
+
suffix = '\\'
|
|
13
|
+
|
|
14
|
+
super(parent, prefix, suffix, false, false)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
def siblings_count
|
|
19
|
+
parent.children.select {|n| n.kind_of?(self.class)}.size
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|