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,76 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
|
|
3
|
+
module RRTF
|
|
4
|
+
# This class represents a specialisation of the Node class to refer to a Node
|
|
5
|
+
# that simply contains text.
|
|
6
|
+
class TextNode < Node
|
|
7
|
+
# Actual text
|
|
8
|
+
attr_accessor :text
|
|
9
|
+
|
|
10
|
+
# This is the constructor for the TextNode class.
|
|
11
|
+
#
|
|
12
|
+
# ==== Parameters
|
|
13
|
+
# parent:: A reference to the Node that owns the TextNode. Must not be
|
|
14
|
+
# nil.
|
|
15
|
+
# text:: A String containing the node text. Defaults to nil.
|
|
16
|
+
#
|
|
17
|
+
# ==== Exceptions
|
|
18
|
+
# RTFError:: Generated whenever an nil parent object is specified to
|
|
19
|
+
# the method.
|
|
20
|
+
def initialize(parent, text=nil)
|
|
21
|
+
super(parent)
|
|
22
|
+
if parent.nil?
|
|
23
|
+
RTFError.fire("Nil parent specified for text node.")
|
|
24
|
+
end
|
|
25
|
+
@parent = parent
|
|
26
|
+
@text = text
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# This method concatenates a String on to the end of the existing text
|
|
30
|
+
# within a TextNode object.
|
|
31
|
+
#
|
|
32
|
+
# ==== Parameters
|
|
33
|
+
# text:: The String to be added to the end of the text node.
|
|
34
|
+
def append(text)
|
|
35
|
+
@text = (@text.nil?) ? text.to_s : @text + text.to_s
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# This method inserts a String into the existing text within a TextNode
|
|
39
|
+
# object. If the TextNode contains no text then it is simply set to the
|
|
40
|
+
# text passed in. If the offset specified is past the end of the nodes
|
|
41
|
+
# text then it is simply appended to the end.
|
|
42
|
+
#
|
|
43
|
+
# ==== Parameters
|
|
44
|
+
# text:: A String containing the text to be added.
|
|
45
|
+
# offset:: The numbers of characters from the first character to insert
|
|
46
|
+
# the new text at.
|
|
47
|
+
def insert(text, offset)
|
|
48
|
+
if !@text.nil?
|
|
49
|
+
@text = @text[0, offset] + text.to_s + @text[offset, @text.length]
|
|
50
|
+
else
|
|
51
|
+
@text = text.to_s
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# This method generates the RTF equivalent for a TextNode object. This
|
|
56
|
+
# method escapes any special sequences that appear in the text.
|
|
57
|
+
def to_rtf
|
|
58
|
+
rtf=(@text.nil? ? '' : @text.gsub("{", "\\{").gsub("}", "\\}").gsub("\\", "\\\\"))
|
|
59
|
+
# This is from lfarcy / rtf-extensions
|
|
60
|
+
# I don't see the point of coding different 128<n<256 range
|
|
61
|
+
|
|
62
|
+
#f1=lambda { |n| n < 128 ? n.chr : n < 256 ? "\\'#{n.to_s(16)}" : "\\u#{n}\\'3f" }
|
|
63
|
+
# Encode as Unicode.
|
|
64
|
+
|
|
65
|
+
f=lambda { |n| n < 128 ? n.chr : "\\u#{n}\\'3f" }
|
|
66
|
+
# Ruby 1.9 is safe, cause detect original encoding
|
|
67
|
+
# and convert text to utf-16 first
|
|
68
|
+
if RUBY_VERSION>"1.9.0"
|
|
69
|
+
return rtf.encode("UTF-16LE", :undef=>:replace).each_codepoint.map(&f).join('')
|
|
70
|
+
else
|
|
71
|
+
# You SHOULD use UTF-8 as input, ok?
|
|
72
|
+
return rtf.unpack('U*').map(&f).join('')
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end # End of the TextNode class.
|
|
76
|
+
end
|
data/lib/rrtf/page.rb
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
module RRTF::Page
|
|
2
|
+
# Represents the left, right, top, and bottom margin in a document page.
|
|
3
|
+
# @author Wesley Hileman
|
|
4
|
+
# @since 1.0.0
|
|
5
|
+
class Margin
|
|
6
|
+
attr_accessor :left, :right, :top, :bottom
|
|
7
|
+
|
|
8
|
+
# Extracts a margin object from a string.
|
|
9
|
+
# @see .parse_string
|
|
10
|
+
#
|
|
11
|
+
# @param (see {.parse_string})
|
|
12
|
+
# @return [Margin] the margin object created from the string.
|
|
13
|
+
# @example Parse margin from a String
|
|
14
|
+
# Margin.from_string("12.1pt, 2.2in, 5cm, 4in")
|
|
15
|
+
# # => #<RRTF::Margin:0x007fa1b3094500 @left=242, @right=3168, @top=2834, @bottom=5760>
|
|
16
|
+
def self.from_string(string)
|
|
17
|
+
self.new(parse_string(string))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Extracts a margin hash from a string.
|
|
21
|
+
# @param [String] string the string from which to parse the margin, taking
|
|
22
|
+
# one of the following formats: "<marg:all>", "<marg:lr>,<marg:tb>", or
|
|
23
|
+
# "<marg:l>,<marg:r>,<marg:t>,<marg:b>" where each number may be suffixed
|
|
24
|
+
# by an optional unit (see {Utilities.value2twips}).
|
|
25
|
+
# @return [Hash<String, Integer>] the margin hash created from the string.
|
|
26
|
+
# @raise [RTFError] if the string cannot be converted into a margin hash.
|
|
27
|
+
# @example Parse margin from a String
|
|
28
|
+
# Margin.parse_string("12.1pt, 2.2in, 5cm, 4in")
|
|
29
|
+
# # => {"top"=>2834, "bottom"=>5760, "left"=>242, "right"=>3168} (twips)
|
|
30
|
+
def self.parse_string(string)
|
|
31
|
+
values = string.split(',').map(&:strip).collect{ |str| RRTF::Utilities.value2twips(str) }
|
|
32
|
+
case values.length
|
|
33
|
+
when 1
|
|
34
|
+
tblr = values.first
|
|
35
|
+
{"top" => tblr, "bottom" => tblr, "left" => tblr, "right" => tblr}
|
|
36
|
+
when 2
|
|
37
|
+
tb = values.last
|
|
38
|
+
lr = values.first
|
|
39
|
+
{"top" => tb, "bottom" => tb, "left" => lr, "right" => lr}
|
|
40
|
+
when 4
|
|
41
|
+
l = values[0]
|
|
42
|
+
r = values[1]
|
|
43
|
+
t = values[2]
|
|
44
|
+
b = values[3]
|
|
45
|
+
{"top" => t, "bottom" => b, "left" => l, "right" => r}
|
|
46
|
+
else
|
|
47
|
+
RRTF::RTFError.fire("Invalid margin '#{string}'.")
|
|
48
|
+
end # case
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Builds a new margin object from a string or hash.
|
|
52
|
+
# @note Margins are stored internally in twentieth points (twips).
|
|
53
|
+
# @see .from_string
|
|
54
|
+
# @see .parse_string
|
|
55
|
+
# @see RRTF::Utilities.value2twips
|
|
56
|
+
#
|
|
57
|
+
# @param [String, Hash] value the value from which to parse the margin.
|
|
58
|
+
# @option value [Integer] "left" the left margin in twips.
|
|
59
|
+
# @option value [Integer] "right" the right margin in twips.
|
|
60
|
+
# @option value [Integer] "top" the top margin in twips.
|
|
61
|
+
# @option value [Integer] "bottom" the bottom margin in twips.
|
|
62
|
+
def initialize(value = nil)
|
|
63
|
+
options = {
|
|
64
|
+
# default 1 inch margins
|
|
65
|
+
"left" => 1440,
|
|
66
|
+
"right" => 1440,
|
|
67
|
+
"top" => 1440,
|
|
68
|
+
"bottom" => 1440
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
case value
|
|
72
|
+
when String
|
|
73
|
+
options = options.merge(self.class.parse_string(value))
|
|
74
|
+
when Hash
|
|
75
|
+
options = options.merge(value)
|
|
76
|
+
when nil
|
|
77
|
+
else
|
|
78
|
+
RRTF::RTFError.fire("Cannot create margin from '#{value}'.")
|
|
79
|
+
end # case
|
|
80
|
+
|
|
81
|
+
@left = options.delete("left")
|
|
82
|
+
@right = options.delete("right")
|
|
83
|
+
@top = options.delete("top")
|
|
84
|
+
@bottom = options.delete("bottom")
|
|
85
|
+
end # initialize
|
|
86
|
+
|
|
87
|
+
def ==(obj)
|
|
88
|
+
obj.class == self.class && obj.state == state
|
|
89
|
+
end
|
|
90
|
+
alias_method :eql?, :==
|
|
91
|
+
|
|
92
|
+
protected
|
|
93
|
+
|
|
94
|
+
def state
|
|
95
|
+
[@left, @top, @right, @bottom]
|
|
96
|
+
end
|
|
97
|
+
end # class Margin
|
|
98
|
+
end # module Page
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
module RRTF::Page
|
|
2
|
+
# Represents the size of a page (width and height).
|
|
3
|
+
# @author Wesley Hileman
|
|
4
|
+
# @since 1.0.0
|
|
5
|
+
class Size
|
|
6
|
+
attr_accessor :width, :height
|
|
7
|
+
|
|
8
|
+
# Dictionary of standard paper sizes in twips.
|
|
9
|
+
DICTIONARY = {
|
|
10
|
+
"A0" => {"width" => 47685, "height" => 67416},
|
|
11
|
+
"A1" => {"width" => 33680, "height" => 47685},
|
|
12
|
+
"A2" => {"width" => 23814, "height" => 33680},
|
|
13
|
+
"A3" => {"width" => 16840, "height" => 23814},
|
|
14
|
+
"A4" => {"width" => 11907, "height" => 16840},
|
|
15
|
+
"A5" => {"width" => 8392, "height" => 11907},
|
|
16
|
+
"LETTER" => {"width" => 12247, "height" => 15819},
|
|
17
|
+
"LEGAL" => {"width" => 12247, "height" => 20185},
|
|
18
|
+
"EXECUTIVE" => {"width" => 10773, "height" => 14402},
|
|
19
|
+
"LEDGER_TABLOID" => {"width" => 15819, "height" => 24494}
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
# Converts a string representing a paper size into a paper object.
|
|
23
|
+
# @see .parse_string
|
|
24
|
+
#
|
|
25
|
+
# @param (see {.parse_string})
|
|
26
|
+
# @return [Size] the paper object representing the width & height of the paper.
|
|
27
|
+
def self.from_string(string)
|
|
28
|
+
self.new(self.parse_string(string))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Converts a string representing a paper size into an ordered pair
|
|
32
|
+
# (a two-item array) representing the width and height of the paper.
|
|
33
|
+
# @see Utilities.value2twips
|
|
34
|
+
#
|
|
35
|
+
# @param [String] string the string to be parsed that can take on one of
|
|
36
|
+
# two formats: (1) "<SIZE>" where SIZE is an entry in {DICTIONARY}
|
|
37
|
+
# or (2) "WIDTH, HEIGHT" where WIDTH and HEIGHT are width and height
|
|
38
|
+
# strings with optional units suffix (see {Utilities.value2twips},
|
|
39
|
+
# default is twips)
|
|
40
|
+
# @return [Hash<String, Integer>] the ordered pair (hash with keys "width" and "height")
|
|
41
|
+
# representing the width and height of the paper.
|
|
42
|
+
def self.parse_string(string)
|
|
43
|
+
# first, try to lookup in size dictionary
|
|
44
|
+
size = DICTIONARY[string]
|
|
45
|
+
return size unless size.nil?
|
|
46
|
+
|
|
47
|
+
# if not found, try to extract width and height from string
|
|
48
|
+
parts = string.split(',').map(&:strip).map{ |str| RRTF::Utilities.value2twips(str) }
|
|
49
|
+
if parts.length == 2
|
|
50
|
+
return {"width" => parts [0], "height" => parts[1]}
|
|
51
|
+
end # if
|
|
52
|
+
|
|
53
|
+
# unable to parse string
|
|
54
|
+
RTFError.fire("Unable to parse size from string '#{string}'.")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# This is the constructor for the Paper class. All dimension parameters
|
|
58
|
+
# to this method are in twips.
|
|
59
|
+
# @note Paper size is stored internally in twentieth points (twips).
|
|
60
|
+
# @see .from_string
|
|
61
|
+
# @see .parse_string
|
|
62
|
+
# @see RRTF::Utilities.value2twips
|
|
63
|
+
#
|
|
64
|
+
# @param [String, Hash] value from which to parse the size.
|
|
65
|
+
# @option value [Integer] "width" the width of the paper in portrait mode (twips).
|
|
66
|
+
def initialize(value = nil)
|
|
67
|
+
# default options
|
|
68
|
+
options = {
|
|
69
|
+
"width" => 12247,
|
|
70
|
+
"height" => 15819
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
case value
|
|
74
|
+
when String
|
|
75
|
+
options = options.merge(self.class.parse_string(value))
|
|
76
|
+
when Hash
|
|
77
|
+
options = options.merge(value)
|
|
78
|
+
when nil
|
|
79
|
+
else
|
|
80
|
+
RRTF::RTFError.fire("Invalid page size format #{value}.")
|
|
81
|
+
end # case
|
|
82
|
+
|
|
83
|
+
@width = options.delete("width")
|
|
84
|
+
@height = options.delete("height")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def ==(obj)
|
|
88
|
+
obj.class == self.class && obj.state == state
|
|
89
|
+
end
|
|
90
|
+
alias_method :eql?, :==
|
|
91
|
+
|
|
92
|
+
protected
|
|
93
|
+
|
|
94
|
+
def state
|
|
95
|
+
[@width, @height]
|
|
96
|
+
end
|
|
97
|
+
end # class Size
|
|
98
|
+
end # module Page
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
|
|
3
|
+
module RRTF
|
|
4
|
+
# This class represents properties that are to be applied at the
|
|
5
|
+
# document level.
|
|
6
|
+
# @author Wesley Hileman
|
|
7
|
+
# @since 1.0.0
|
|
8
|
+
class DocumentProperties < Properties
|
|
9
|
+
include DocumentFormatting
|
|
10
|
+
include PageFormatting
|
|
11
|
+
|
|
12
|
+
# This is a constructor for the DocumentProperties class.
|
|
13
|
+
#
|
|
14
|
+
# @param [Hash] options
|
|
15
|
+
# @option options (see {DocumentFormatting#initialize_document_formatting})
|
|
16
|
+
# @option options (see {PageFormatting#initialize_page_formatting})
|
|
17
|
+
def initialize(options = {})
|
|
18
|
+
initialize_document_formatting(options)
|
|
19
|
+
initialize_page_formatting(options)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Converts a document properties object into an RTF sequence.
|
|
23
|
+
#
|
|
24
|
+
# @return [String] the RTF sequence corresponding to the properties object.
|
|
25
|
+
def to_rtf
|
|
26
|
+
rtf = StringIO.new
|
|
27
|
+
|
|
28
|
+
rtf << document_formatting_to_rtf
|
|
29
|
+
rtf << page_formatting_to_rtf
|
|
30
|
+
|
|
31
|
+
rtf.string
|
|
32
|
+
end
|
|
33
|
+
end # End of the DocumentProperties class
|
|
34
|
+
end # module RRTF
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
|
|
3
|
+
module RRTF
|
|
4
|
+
# This class represents properties that are to be applied to geometry
|
|
5
|
+
# objects.
|
|
6
|
+
# @author Wesley Hileman
|
|
7
|
+
# @since 1.0.0
|
|
8
|
+
class GeometryProperties < Properties
|
|
9
|
+
|
|
10
|
+
HORIZONTAL_REFERENCE_DICTIONARY = {
|
|
11
|
+
"MARGIN" => 0,
|
|
12
|
+
"PAGE" => 1,
|
|
13
|
+
"COLUMN" => 2,
|
|
14
|
+
"CHARACTER" => 3,
|
|
15
|
+
"LEFT_MARGIN" => 4,
|
|
16
|
+
"RIGHT_MARGIN" => 5,
|
|
17
|
+
"INSIDE_MARGIN" => 6,
|
|
18
|
+
"OUTSIDE_MARGIN" => 7
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
VERTICAL_REFERENCE_DICTIONARY = {
|
|
22
|
+
"MARGIN" => 0,
|
|
23
|
+
"PAGE" => 1,
|
|
24
|
+
"PARAGRAPH" => 2,
|
|
25
|
+
"LINE" => 3,
|
|
26
|
+
"TOP_MARGIN" => 4,
|
|
27
|
+
"BOTTOM_MARGIN" => 5,
|
|
28
|
+
"INSIDE_MARGIN" => 6,
|
|
29
|
+
"OUTSIDE_MARGIN" => 7
|
|
30
|
+
}.freeze
|
|
31
|
+
|
|
32
|
+
HORIZONTAL_ALIGNMENT_DICTIONARY = {
|
|
33
|
+
"ABSOLUTE" => 0,
|
|
34
|
+
"LEFT" => 1,
|
|
35
|
+
"CENTER" => 2,
|
|
36
|
+
"RIGHT" => 3,
|
|
37
|
+
"INSIDE" => 4,
|
|
38
|
+
"OUTSIDE" => 5
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
VERTICAL_ALIGNMENT_DICTIONARY = {
|
|
42
|
+
"ABSOLUTE" => 0,
|
|
43
|
+
"TOP" => 1,
|
|
44
|
+
"CENTER" => 2,
|
|
45
|
+
"BOTTOM" => 3,
|
|
46
|
+
"INSIDE" => 4,
|
|
47
|
+
"OUTSIDE" => 5
|
|
48
|
+
}.freeze
|
|
49
|
+
|
|
50
|
+
WIDTH_REFERENCE_DICTIONARY = {
|
|
51
|
+
"MARGIN" => 0,
|
|
52
|
+
"PAGE" => 1,
|
|
53
|
+
"LEFT_MARGIN" => 2,
|
|
54
|
+
"RIGHT_MARGIN" => 3,
|
|
55
|
+
"INSIDE_MARGIN" => 4,
|
|
56
|
+
"OUTSIDE_MARGIN" => 5
|
|
57
|
+
}.freeze
|
|
58
|
+
|
|
59
|
+
HEIGHT_REFERENCE_DICTIONARY = {
|
|
60
|
+
"MARGIN" => 0,
|
|
61
|
+
"PAGE" => 1,
|
|
62
|
+
"TOP_MARGIN" => 2,
|
|
63
|
+
"BOTTOM_MARGIN" => 3,
|
|
64
|
+
"INSIDE_MARGIN" => 4,
|
|
65
|
+
"OUTSIDE_MARGIN" => 5
|
|
66
|
+
}.freeze
|
|
67
|
+
|
|
68
|
+
TEXT_WRAP_DICTIONARY = {
|
|
69
|
+
"INLINE" => {"WRAP" => 1, "SIDE" => nil},
|
|
70
|
+
"AROUND_BOTH" => {"WRAP" => 2, "SIDE" => 0},
|
|
71
|
+
"AROUND_LEFT" => {"WRAP" => 2, "SIDE" => 1},
|
|
72
|
+
"AROUND_RIGHT" => {"WRAP" => 2, "SIDE" => 2},
|
|
73
|
+
"AROUND_LARGEST" => {"WRAP" => 2, "SIDE" => 3},
|
|
74
|
+
"NONE" => {"WRAP" => 3, "SIDE" => nil},
|
|
75
|
+
"TIGHT_AROUND_BOTH" => {"WRAP" => 4, "SIDE" => 0},
|
|
76
|
+
"TIGHT_AROUND_LEFT" => {"WRAP" => 4, "SIDE" => 1},
|
|
77
|
+
"TIGHT_AROUND_RIGHT" => {"WRAP" => 4, "SIDE" => 2},
|
|
78
|
+
"TIGHT_AROUND_LARGEST" => {"WRAP" => 4, "SIDE" => 3},
|
|
79
|
+
}.freeze
|
|
80
|
+
|
|
81
|
+
GEOMERTY_TYPE_DICTIONARY = {
|
|
82
|
+
"CUSTOM" => 0,
|
|
83
|
+
"RECTANGLE" => 1,
|
|
84
|
+
"ROUND_RECTANGLE" => 2,
|
|
85
|
+
"ELLIPSE" => 3,
|
|
86
|
+
"DIAMOND" => 4,
|
|
87
|
+
"ISOSCELES_TRIANGLE" => 5,
|
|
88
|
+
"RIGHT_TRIANGLE" => 6,
|
|
89
|
+
"PARALLELOGRAM" => 7,
|
|
90
|
+
"TRAPEZOID" => 8,
|
|
91
|
+
"HEXAGON" => 9,
|
|
92
|
+
"OCTAGON" => 10,
|
|
93
|
+
"PENTAGON" => 56,
|
|
94
|
+
"LINE" => 20,
|
|
95
|
+
"TEXT_BOX" => 202
|
|
96
|
+
}.freeze
|
|
97
|
+
|
|
98
|
+
TEXT_ANCHOR_DICTIONARY = {
|
|
99
|
+
"TOP" => 0,
|
|
100
|
+
"MIDDLE" => 1,
|
|
101
|
+
"BOTTOM" => 2,
|
|
102
|
+
"TOP_CENTERED" => 3,
|
|
103
|
+
"MIDDLE_CENTERED" => 4,
|
|
104
|
+
"BOTTOM_CENTERED" => 5,
|
|
105
|
+
"TOP_BASELINE" => 6,
|
|
106
|
+
"BOTTOM_BASELINE" => 7,
|
|
107
|
+
"TOP_CENTERED_BASELINE" => 8,
|
|
108
|
+
"BOTTOM_CENTERED_BASELINE" => 9
|
|
109
|
+
}.freeze
|
|
110
|
+
|
|
111
|
+
# @note The upper three bits store segment stype, lower 13 bits store the
|
|
112
|
+
# number of segments of that type to appear in series (always 1 -- except
|
|
113
|
+
# for control segments -- for the non-compressed encoding used here
|
|
114
|
+
# where the codes for segments of the same type that appear in series are
|
|
115
|
+
# repeated).
|
|
116
|
+
PATH_SEGMENT_DICTIONARY = {
|
|
117
|
+
# draw a line from the current point to a specified end point
|
|
118
|
+
# [requires one additional point]
|
|
119
|
+
"LINE_TO" => "0001".to_i(16),
|
|
120
|
+
# draw a cubic bezier curve using the current point, two control points,
|
|
121
|
+
# and an end point [requires three additional points]
|
|
122
|
+
"CUBIC_BEZIER_TO" => "2001".to_i(16),
|
|
123
|
+
# draw a line from the current point to the starting point and close
|
|
124
|
+
# the path [requires no additional points]
|
|
125
|
+
"CLOSE_PATH" => "6001".to_i(16),
|
|
126
|
+
# start a path (control segment) [requires one point]
|
|
127
|
+
"START_AT" => "4000".to_i(16),
|
|
128
|
+
# end a path (control segment) [requires no points]
|
|
129
|
+
"END" => "8000".to_i(16)
|
|
130
|
+
}.freeze
|
|
131
|
+
|
|
132
|
+
# This is a constructor for the GeometryProperties class.
|
|
133
|
+
#
|
|
134
|
+
# @param [Hash] options
|
|
135
|
+
def initialize(options = {})
|
|
136
|
+
@type = GEOMERTY_TYPE_DICTIONARY[options.delete("type")]
|
|
137
|
+
@rotation = Utilities.value2geomfrac(options.delete("rotate"))
|
|
138
|
+
@left = Utilities.value2twips(options.delete("left"))
|
|
139
|
+
@right = Utilities.value2twips(options.delete("right"))
|
|
140
|
+
@top = Utilities.value2twips(options.delete("top"))
|
|
141
|
+
@bottom = Utilities.value2twips(options.delete("bottom"))
|
|
142
|
+
@z_index = options.delete("z_index")
|
|
143
|
+
@horizontal_reference = HORIZONTAL_REFERENCE_DICTIONARY[options.delete("horizontal_reference")] || HORIZONTAL_REFERENCE_DICTIONARY["MARGIN"]
|
|
144
|
+
@vertical_reference = VERTICAL_REFERENCE_DICTIONARY[options.delete("vertical_reference")] || VERTICAL_REFERENCE_DICTIONARY["MARGIN"]
|
|
145
|
+
@text_wrap = TEXT_WRAP_DICTIONARY[options.delete("text_wrap")]
|
|
146
|
+
@below_text = Utilities.value2geombool(options.delete("below_text"))
|
|
147
|
+
@lock_anchor = options.delete("lock_anchor")
|
|
148
|
+
@horizontal_alignment = HORIZONTAL_ALIGNMENT_DICTIONARY[options.delete("horizontal_alignment")] || HORIZONTAL_ALIGNMENT_DICTIONARY["ABSOLUTE"]
|
|
149
|
+
@vertical_alignment = VERTICAL_ALIGNMENT_DICTIONARY[options.delete("vertical_alignment")] || VERTICAL_ALIGNMENT_DICTIONARY["ABSOLUTE"]
|
|
150
|
+
@allow_overlap = Utilities.value2geombool(options.delete("allow_overlap"))
|
|
151
|
+
@width_reference = WIDTH_REFERENCE_DICTIONARY[options.delete("width_reference")] || WIDTH_REFERENCE_DICTIONARY["MARGIN"]
|
|
152
|
+
@height_reference = HEIGHT_REFERENCE_DICTIONARY[options.delete("height_reference")] || HEIGHT_REFERENCE_DICTIONARY["MARGIN"]
|
|
153
|
+
@width, @width_units = Utilities.parse_string_with_units(options.delete("width"))
|
|
154
|
+
@height, @height_units = Utilities.parse_string_with_units(options.delete("height"))
|
|
155
|
+
@width = Utilities.value2twips("#{@width}#{@width_units}") unless @width.nil? || @width_units == '%'
|
|
156
|
+
@height = Utilities.value2twips("#{@height}#{@height_units}") unless @height.nil? || @height_units == '%'
|
|
157
|
+
@fill_color = options.delete("fill_color")
|
|
158
|
+
@has_fill = Utilities.value2geombool(options.delete("has_fill") || !@fill_color.nil?)
|
|
159
|
+
@line_color = options.delete("line_color")
|
|
160
|
+
@line_width = Utilities.value2emu(options.delete("line_width"))
|
|
161
|
+
@has_line = Utilities.value2geombool(options.delete("has_line") || !@line_color.nil? || !@line_width.nil?)
|
|
162
|
+
@text_margin = options.delete("text_margin")
|
|
163
|
+
@text_anchor = TEXT_ANCHOR_DICTIONARY[options.delete("text_anchor")]
|
|
164
|
+
@fit_to_text = Utilities.value2geombool(options.delete("fit_to_text"))
|
|
165
|
+
@fit_text_to_shape = Utilities.value2geombool(options.delete("fit_text_to_shape"))
|
|
166
|
+
@flip_horizontal = Utilities.value2geombool(options.delete("flip_horizontal"))
|
|
167
|
+
@flip_vertical = Utilities.value2geombool(options.delete("flip_vertical"))
|
|
168
|
+
@path = options.delete("path")
|
|
169
|
+
@path_coordinate_origin = options.delete("path_coordinate_origin") || [0, 0]
|
|
170
|
+
@path_coordinate_limits = options.delete("path_coordinate_limits") || [21600, 21600]
|
|
171
|
+
|
|
172
|
+
parse_dimensions!
|
|
173
|
+
parse_color! :fill_color, :line_color
|
|
174
|
+
parse_margin! :text_margin
|
|
175
|
+
parse_path!
|
|
176
|
+
|
|
177
|
+
unless options.empty?
|
|
178
|
+
RTFError.fire("Unreconized geometry options #{options}.")
|
|
179
|
+
end # unless
|
|
180
|
+
end # initialize()
|
|
181
|
+
|
|
182
|
+
# Converts a geometry properties object into an RTF sequence.
|
|
183
|
+
#
|
|
184
|
+
# @return [String] the RTF sequence corresponding to the properties object.
|
|
185
|
+
def to_rtf
|
|
186
|
+
rtf = StringIO.new
|
|
187
|
+
|
|
188
|
+
# keyword properties
|
|
189
|
+
rtf << "\\shpleft#{@left}" unless @left.nil?
|
|
190
|
+
rtf << "\\shpright#{@right}" unless @right.nil?
|
|
191
|
+
rtf << "\\shptop#{@top}" unless @top.nil?
|
|
192
|
+
rtf << "\\shpbottom#{@bottom}" unless @bottom.nil?
|
|
193
|
+
rtf << "\\shpz#{@z_index}" unless @z_index.nil?
|
|
194
|
+
rtf << "\\shpbxpage" if @horizontal_reference == HORIZONTAL_REFERENCE_DICTIONARY["PAGE"]
|
|
195
|
+
rtf << "\\shpbxmargin" if @horizontal_reference == HORIZONTAL_REFERENCE_DICTIONARY["MARGIN"]
|
|
196
|
+
rtf << "\\shpbxcolumn" if @horizontal_reference == HORIZONTAL_REFERENCE_DICTIONARY["COLUMN"]
|
|
197
|
+
rtf << "\\shpbxignore" unless @vertical_reference.nil?
|
|
198
|
+
rtf << "\\shpbypage" if @vertical_reference == VERTICAL_REFERENCE_DICTIONARY["PAGE"]
|
|
199
|
+
rtf << "\\shpbymargin" if @vertical_reference == VERTICAL_REFERENCE_DICTIONARY["MARGIN"]
|
|
200
|
+
rtf << "\\shpbypara" if @vertical_reference == VERTICAL_REFERENCE_DICTIONARY["PARAGRAPH"]
|
|
201
|
+
rtf << "\\shpbyignore" unless @vertical_reference.nil?
|
|
202
|
+
rtf << "\\shpwr#{@text_wrap["WRAP"]}" unless @text_wrap.nil? || @text_wrap["WRAP"].nil?
|
|
203
|
+
rtf << "\\shpwrk#{@text_wrap["SIDE"]}" unless @text_wrap.nil? || @text_wrap["SIDE"].nil?
|
|
204
|
+
rtf << "\\shpfblwtxt#{@below_text}" unless @below_text.nil?
|
|
205
|
+
rtf << "\\shplockanchor" if @lock_anchor
|
|
206
|
+
|
|
207
|
+
rtf << "\n"
|
|
208
|
+
|
|
209
|
+
# object properties
|
|
210
|
+
rtf << build_property("shapeType", @type) unless @type.nil?
|
|
211
|
+
rtf << build_property("rotation", @rotation) unless @rotation.nil?
|
|
212
|
+
rtf << build_property("posh", @horizontal_alignment) unless @horizontal_alignment.nil?
|
|
213
|
+
rtf << build_property("posrelh", @horizontal_reference) unless @horizontal_reference.nil?
|
|
214
|
+
rtf << build_property("posv", @vertical_alignment) unless @vertical_alignment.nil?
|
|
215
|
+
rtf << build_property("posrelv", @vertical_reference) unless @vertical_reference.nil?
|
|
216
|
+
rtf << build_property("fAllowOverlap", @allow_overlap) unless @allow_overlap.nil?
|
|
217
|
+
rtf << build_property("pctHoriz", @width) unless @width.nil? || @width_units != '%'
|
|
218
|
+
rtf << build_property("pctVert", @height) unless @height.nil? || @height_units != '%'
|
|
219
|
+
rtf << build_property("sizerelh", @width_reference) unless @width_reference.nil?
|
|
220
|
+
rtf << build_property("sizerelv", @height_reference) unless @height_reference.nil?
|
|
221
|
+
rtf << build_property("fFilled", @has_fill) unless @has_fill.nil?
|
|
222
|
+
rtf << build_property("fillColor", @fill_color) unless @fill_color.nil?
|
|
223
|
+
rtf << build_property("fLine", @has_line) unless @has_fill.nil?
|
|
224
|
+
rtf << build_property("lineColor", @line_color) unless @line_color.nil?
|
|
225
|
+
rtf << build_property("lineWidth", @line_width) unless @line_width.nil?
|
|
226
|
+
rtf << build_property("dxTextLeft", @text_margin_left) unless @text_margin_left.nil?
|
|
227
|
+
rtf << build_property("dxTextRight", @text_margin_right) unless @text_margin_right.nil?
|
|
228
|
+
rtf << build_property("dyTextTop", @text_margin_top) unless @text_margin_top.nil?
|
|
229
|
+
rtf << build_property("dyTextBottom", @text_margin_bottom) unless @text_margin_bottom.nil?
|
|
230
|
+
rtf << build_property("anchorText", @text_anchor) unless @text_anchor.nil?
|
|
231
|
+
rtf << build_property("fBehindDocument", @below_text) unless @below_text.nil?
|
|
232
|
+
rtf << build_property("fFitShapeToText", @fit_to_text) unless @fit_to_text.nil?
|
|
233
|
+
rtf << build_property("fFitTextToShape", @fit_text_to_shape) unless @fit_text_to_shape.nil?
|
|
234
|
+
rtf << build_property("fFlipH", @flip_horizontal) unless @flip_horizontal.nil?
|
|
235
|
+
rtf << build_property("fFlipV", @flip_vertical) unless @flip_vertical.nil?
|
|
236
|
+
rtf << build_property("geoLeft", @path_coordinate_origin[0]) unless @path.nil? || @path_coordinate_origin.nil?
|
|
237
|
+
rtf << build_property("geoTop", @path_coordinate_origin[1]) unless @path.nil? || @path_coordinate_origin.nil?
|
|
238
|
+
rtf << build_property("geoRight", @path_coordinate_limits[0]) unless @path.nil? || @path_coordinate_limits.nil?
|
|
239
|
+
rtf << build_property("geoBottom", @path_coordinate_limits[1]) unless @path.nil? || @path_coordinate_limits.nil?
|
|
240
|
+
rtf << build_property("pVerticies", @path_verticies) unless @path.nil? || @path_verticies.nil?
|
|
241
|
+
rtf << build_property("pSegmentInfo", @path_segment_info) unless @path.nil? || @path_segment_info.nil?
|
|
242
|
+
rtf << build_property("pConnectionSites", @path_connection_sites) unless @path.nil? || @path_connection_sites.nil?
|
|
243
|
+
rtf << build_property("fLineOK", 1)
|
|
244
|
+
rtf << build_property("fFillOK", 1)
|
|
245
|
+
rtf << build_property("f3DOK", 1)
|
|
246
|
+
|
|
247
|
+
rtf.string
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
private
|
|
251
|
+
|
|
252
|
+
def build_property(name, value)
|
|
253
|
+
"{\\sp{\\sn #{name}}{\\sv #{value}}}\n"
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def build_array(array, bytes_per_element)
|
|
257
|
+
"#{bytes_per_element};#{array.length};#{array.join(';')}"
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def array2emu(array)
|
|
261
|
+
array.collect{ |el| Utilities.value2emu(el) }
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def parse_dimensions!
|
|
265
|
+
unless @width.nil?
|
|
266
|
+
if @width_units == '%'
|
|
267
|
+
@percent_width = @width
|
|
268
|
+
else
|
|
269
|
+
case [@left.nil?, @right.nil?]
|
|
270
|
+
when [true, true]
|
|
271
|
+
@left = 0
|
|
272
|
+
@right = @width
|
|
273
|
+
when [true, false]
|
|
274
|
+
@left = @right - @width
|
|
275
|
+
when [false, true]
|
|
276
|
+
@right = @left + @width
|
|
277
|
+
end # case
|
|
278
|
+
end # if
|
|
279
|
+
end # unless
|
|
280
|
+
|
|
281
|
+
unless @height.nil?
|
|
282
|
+
if @height_units == '%'
|
|
283
|
+
@percent_height = @height
|
|
284
|
+
else
|
|
285
|
+
case [@top.nil?, @bottom.nil?]
|
|
286
|
+
when [true, true]
|
|
287
|
+
@top = 0
|
|
288
|
+
@bottom = @height
|
|
289
|
+
when [true, false]
|
|
290
|
+
@top = @bottom - @height
|
|
291
|
+
when [false, true]
|
|
292
|
+
@bottom = @top + @height
|
|
293
|
+
end # case
|
|
294
|
+
end # if
|
|
295
|
+
end # unless
|
|
296
|
+
end # parse_dimensions()
|
|
297
|
+
|
|
298
|
+
def parse_color!(*color_attrs)
|
|
299
|
+
color_attrs.each do |color_attr|
|
|
300
|
+
color = instance_variable_get(:"@#{color_attr}")
|
|
301
|
+
|
|
302
|
+
unless color.nil?
|
|
303
|
+
case color
|
|
304
|
+
when String
|
|
305
|
+
color = Colour.from_string(color).to_decimal("reverse_bytes" => true)
|
|
306
|
+
when Colour
|
|
307
|
+
color = color.to_decimal
|
|
308
|
+
else
|
|
309
|
+
RTFError.fire("Unsupported color format #{color}.")
|
|
310
|
+
end # case
|
|
311
|
+
end # unless
|
|
312
|
+
|
|
313
|
+
instance_variable_set(:"@#{color_attr}", color)
|
|
314
|
+
end # each
|
|
315
|
+
end # parse_color()
|
|
316
|
+
|
|
317
|
+
def parse_margin!(*margin_attrs)
|
|
318
|
+
margin_attrs.each do |margin_attr|
|
|
319
|
+
margin = instance_variable_get("@#{margin_attr}")
|
|
320
|
+
next if margin.nil?
|
|
321
|
+
|
|
322
|
+
margin = Page::Margin.new(margin)
|
|
323
|
+
left = Utilities.value2emu(margin.left+'twip')
|
|
324
|
+
right = Utilities.value2emu(margin.right+'twip')
|
|
325
|
+
top = Utilities.value2emu(margin.top+'twip')
|
|
326
|
+
bottom = Utilities.value2emu(margin.bottom+'twip')
|
|
327
|
+
|
|
328
|
+
instance_variable_set("@#{margin_attr}", margin)
|
|
329
|
+
instance_variable_set("@#{margin_attr}_left", left)
|
|
330
|
+
instance_variable_set("@#{margin_attr}_right", right)
|
|
331
|
+
instance_variable_set("@#{margin_attr}_top", top)
|
|
332
|
+
instance_variable_set("@#{margin_attr}_bottom", bottom)
|
|
333
|
+
end
|
|
334
|
+
end # parse_margin()
|
|
335
|
+
|
|
336
|
+
def parse_path!
|
|
337
|
+
return if @path.nil?
|
|
338
|
+
|
|
339
|
+
verticies = []
|
|
340
|
+
connection_sites = []
|
|
341
|
+
seg_info = []
|
|
342
|
+
|
|
343
|
+
unless @path.is_a?(Array) && @path.collect{ |tup| tup.is_a?(Array) && (1..5).include?(tup.length) }.all?
|
|
344
|
+
RTFError.fire("Path segments must be an array of arrays with length 1 through 5.")
|
|
345
|
+
end # unless
|
|
346
|
+
|
|
347
|
+
sx = (@path_coordinate_limits[0] - @path_coordinate_origin[0]).to_f/(Utilities.value2emu("#{@width}twip")).to_f
|
|
348
|
+
sy = (@path_coordinate_limits[1] - @path_coordinate_origin[1]).to_f/(Utilities.value2emu("#{@height}twip")).to_f
|
|
349
|
+
|
|
350
|
+
@path.each do |seg|
|
|
351
|
+
# first item in segment array gives the segment type
|
|
352
|
+
type = seg[0]
|
|
353
|
+
|
|
354
|
+
if PATH_SEGMENT_DICTIONARY[type].nil?
|
|
355
|
+
RTFError.fire("Invalid segment type '#{type}'.")
|
|
356
|
+
end # case
|
|
357
|
+
|
|
358
|
+
if seg.length > 1
|
|
359
|
+
# remaining items give the points associated with the segment
|
|
360
|
+
# (last element is the end point; bezier curves also have a control
|
|
361
|
+
# point before last point; the starting point is given by the end
|
|
362
|
+
# point of the last segment)
|
|
363
|
+
points = seg[1..(seg.length - 1)].collect{ |p| array2emu(p) }.collect{ |p| [(p[0]*sx).round, (p[1]*sy).round] }
|
|
364
|
+
verticies += points
|
|
365
|
+
# the last point is the endpoint for the segment that forms a
|
|
366
|
+
# "connection site" with the next segment
|
|
367
|
+
connection_sites << points.last
|
|
368
|
+
end # if
|
|
369
|
+
|
|
370
|
+
# add appropriate code to the segment information array indicating the
|
|
371
|
+
# type of segment to create
|
|
372
|
+
seg_info << PATH_SEGMENT_DICTIONARY[type]
|
|
373
|
+
end # each
|
|
374
|
+
|
|
375
|
+
@path_verticies = build_array(verticies.collect{ |v| "(#{v[0]},#{v[1]})" }, 8)
|
|
376
|
+
@path_connection_sites = build_array(connection_sites.collect{ |s| "(#{s[0]},#{s[1]})" }, 8)
|
|
377
|
+
@path_segment_info = build_array(seg_info, 2)
|
|
378
|
+
end # parse_path()
|
|
379
|
+
end # End of the DocumentProperties class
|
|
380
|
+
end # module RRTF
|