rtf 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/CHANGES +5 -0
  2. data/LICENSE +20 -0
  3. data/{doc/README → README.rdoc} +10 -2
  4. data/Rakefile +47 -0
  5. data/VERSION.yml +5 -0
  6. data/examples/example01.rb +0 -0
  7. data/examples/example02.rb +0 -0
  8. data/examples/example03.rb +4 -4
  9. data/examples/example03.rtf +0 -0
  10. data/examples/example04.rb +50 -0
  11. data/examples/rubyrtf.bmp +0 -0
  12. data/examples/rubyrtf.jpg +0 -0
  13. data/examples/rubyrtf.png +0 -0
  14. data/lib/rtf.rb +0 -0
  15. data/lib/rtf/colour.rb +0 -0
  16. data/lib/rtf/font.rb +0 -0
  17. data/lib/rtf/information.rb +2 -3
  18. data/lib/rtf/node.rb +294 -29
  19. data/lib/rtf/paper.rb +0 -0
  20. data/lib/rtf/style.rb +0 -0
  21. data/test/{CharacterStyleTest.rb → character_style_test.rb} +1 -7
  22. data/test/{ColourTableTest.rb → colour_table_test.rb} +92 -97
  23. data/test/{ColourTest.rb → colour_test.rb} +116 -122
  24. data/test/{CommandNodeTest.rb → command_node_test.rb} +1 -7
  25. data/test/{ContainerNodeTest.rb → container_node_test.rb} +3 -9
  26. data/test/{DocumentStyleTest.rb → document_style_test.rb} +1 -7
  27. data/test/{DocumentTest.rb → document_test.rb} +1 -7
  28. data/test/fixtures/bitmap1.bmp +0 -0
  29. data/test/fixtures/bitmap2.bmp +0 -0
  30. data/test/fixtures/jpeg1.jpg +0 -0
  31. data/test/fixtures/jpeg2.jpg +0 -0
  32. data/test/fixtures/png1.png +0 -0
  33. data/test/fixtures/png2.png +0 -0
  34. data/test/{FontTableTest.rb → font_table_test.rb} +1 -6
  35. data/test/{FontTest.rb → font_test.rb} +1 -6
  36. data/test/{FooterNodeTest.rb → footer_node_test.rb} +1 -7
  37. data/test/{HeaderNodeTest.rb → header_node_test.rb} +1 -7
  38. data/test/image_node_test.rb +125 -0
  39. data/test/{InformationTest.rb → information_test.rb} +1 -7
  40. data/test/{NodeTest.rb → node_test.rb} +4 -10
  41. data/test/{ParagraphStyleTest.rb → paragraph_style_test.rb} +1 -7
  42. data/test/{StyleTest.rb → style_test.rb} +1 -7
  43. data/test/{TableCellNodeTest.rb → table_cell_node_test.rb} +1 -7
  44. data/test/{TableNodeTest.rb → table_node_test.rb} +1 -7
  45. data/test/{TableRowNodeTest.rb → table_row_node_test.rb} +1 -7
  46. data/test/test_helper.rb +13 -0
  47. data/test/{TextNodeTest.rb → text_node_test.rb} +4 -10
  48. metadata +119 -70
  49. data/doc/makedoc.bat +0 -2
  50. data/test/run.bat +0 -2
  51. data/test/unittest.bat +0 -2
  52. data/test/unittest.rb +0 -21
data/CHANGES ADDED
@@ -0,0 +1,5 @@
1
+ == CHANGES
2
+
3
+ 0.3.0
4
+ * Resolve problems on Ruby 1.9.1 with ImageNode#read_source. Peter uses IO#getc, which returns a String on Ruby 1.9.1, not a Integer, so I replaced with getbyte. [clbustos]
5
+ * Deleted duplicated definition of ImageNode#style= with attr_writer and def. [clbustos]
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Peter Wood
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,4 +1,4 @@
1
- == Ruby Rich Text Format (RTF) Library 0.1.0
1
+ == Ruby Rich Text Format (RTF) Library
2
2
  The RTF library provides a pure Ruby set of functionality that can be used to
3
3
  programmatically create RTF documents. The main aim in developing this library
4
4
  is to ease the complexity involved in assembling RTF documents although some
@@ -174,4 +174,12 @@ There you have it. You've been given a quick overview of the basics of using
174
174
  the library. For more information consult the HTML based API documentation that
175
175
  is installed with the library gem (if you're reading this you may already be
176
176
  looking at this documentation). Another source of information is the examples
177
- directory, so check that out too.
177
+ directory, so check that out too.
178
+
179
+ CONTRIBUTORS
180
+
181
+ Claudio Bustos
182
+ Chris O'Sullivan
183
+
184
+ COPYRIGHT
185
+ Copyright (c) 2009-2011 Peter Wood. See LICENSE for details.
@@ -0,0 +1,47 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |s|
6
+ s.name = "rtf"
7
+ s.summary = 'Ruby library to create rich text format documents.'
8
+ s.email = "paw220470@yahoo.ie"
9
+ s.homepage = "http://github.com/thechrisoshow/rtf"
10
+ s.description = 'Ruby RTF is a library that can be used to create '\
11
+ 'rich text format (RTF) documents. RTF is a text '\
12
+ 'based standard for laying out document content.'
13
+ s.authors = ["Peter Wood"]
14
+ s.files = FileList["[A-Z]*", "{examples,lib,test}/**/*"]
15
+ end
16
+ rescue LoadError
17
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
18
+ end
19
+
20
+ require 'rake/rdoctask'
21
+ Rake::RDocTask.new do |rdoc|
22
+ rdoc.rdoc_dir = 'rdoc'
23
+ rdoc.title = 'ruby-rtf'
24
+ rdoc.options << '--line-numbers' << '--inline-source'
25
+ rdoc.rdoc_files.include('[A-Z]*')
26
+ rdoc.rdoc_files.include('lib/**/*.rb')
27
+ end
28
+
29
+ require 'rake/testtask'
30
+ Rake::TestTask.new(:test) do |t|
31
+ t.libs << 'lib' << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+ begin
37
+ require 'rcov/rcovtask'
38
+ Rcov::RcovTask.new do |t|
39
+ t.libs << 'test'
40
+ t.test_files = FileList['test/**/*_test.rb']
41
+ t.verbose = true
42
+ end
43
+ rescue LoadError
44
+ puts "RCov is not available. In order to run rcov, you must: sudo gem install rcov"
45
+ end
46
+
47
+ task :default => :test
@@ -0,0 +1,5 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 3
4
+ :patch: 0
5
+ :build:
File without changes
File without changes
@@ -32,9 +32,9 @@ end
32
32
 
33
33
  document.paragraph(styles['PS_NORMAL']) do |p1|
34
34
  p1 << 'This document is automatically generated using the RTF Ruby '
35
- p1 << 'library by Peter Wood. This serves as an example of what can '
36
- p1 << 'be achieved in document creation via the library. The source '
37
- p1 << 'code that was used to generate it is listed below...'
35
+ p1 << 'library. This serves as an example of what can be achieved '
36
+ p1 << 'in document creation via the library. The source code that '
37
+ p1 << 'was used to generate it is listed below...'
38
38
  end
39
39
 
40
40
  document.paragraph(styles['PS_INDENTED']) do |p1|
@@ -60,7 +60,7 @@ end
60
60
  document.paragraph(styles['PS_NORMAL']) do |p1|
61
61
  p1 << "This example shows the creation of a new document and the "
62
62
  p1 << "of textual content to it. The example also provides examples "
63
- p1 << "of using block scope to delimit style scope (lines 35-40)."
63
+ p1 << "of using block scope to delimit style scope (lines 40-51)."
64
64
  end
65
65
 
66
66
  File.open('example03.rtf', 'w') {|file| file.write(document.to_rtf)}
File without changes
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'rtf'
5
+
6
+ include RTF
7
+
8
+ IMAGE_FILE = 'rubyrtf.png'
9
+
10
+ begin
11
+ document = Document.new(Font.new(Font::ROMAN, 'Times New Roman'))
12
+
13
+ # Add some text to the document and then add the scaled image.
14
+ document.paragraph do |p|
15
+ p << "This is a simple document that attempts to demonstrate the use "
16
+ p << "of images in a document. A simple image should appear in the page "
17
+ p << "header above, on the right hand side. The same image, scaled to "
18
+ p << "four times its normal size, should appear below this text."
19
+ p.line_break
20
+ end
21
+
22
+ # Add the scaled image.
23
+ image = document.image(IMAGE_FILE)
24
+ image.x_scaling = 400
25
+ image.y_scaling = 400
26
+
27
+ # Add some follow up text.
28
+ document.paragraph do |p|
29
+ p.line_break
30
+ p << "Due to the way images are stored in RTF documents, adding images "
31
+ p << "to a document can result in the document file becoming very large. "
32
+ p << "The Ruby RTF library supports the addition of images in the PNG, "
33
+ p << "JPEG and Windows device independent bitmap formats. A compressed "
34
+ p << "image format (like PNG or JPEG) is preferrable to the plain bitmap "
35
+ p << "format as this will result in a smaller document file."
36
+ end
37
+
38
+ # Add a header to the document.
39
+ style = ParagraphStyle.new
40
+ style.justification = ParagraphStyle::RIGHT_JUSTIFY
41
+ header = HeaderNode.new(document)
42
+ header.paragraph(style) {|n| n.image(IMAGE_FILE)}
43
+ document.header = header
44
+
45
+ # Write the document to a file.
46
+ File.open('example04.rtf', 'w') {|file| file.write(document.to_rtf)}
47
+ rescue => error
48
+ puts "ERROR: #{error.message}"
49
+ error.backtrace.each {|step| puts " #{step}"}
50
+ end
Binary file
Binary file
Binary file
data/lib/rtf.rb CHANGED
File without changes
File without changes
File without changes
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'stringio'
4
- require 'parsedate'
5
-
4
+ require 'date'
6
5
  module RTF
7
6
  # This class represents an information group for a RTF document.
8
7
  class Information
@@ -56,7 +55,7 @@ module RTF
56
55
  if setting.instance_of?(Time)
57
56
  @created = setting
58
57
  else
59
- datetime = ParseDate::parsedate(setting.to_s)
58
+ datetime = Date._parse(setting.to_s).values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :wday)
60
59
  if datetime == nil
61
60
  RTFError.fire("Invalid document creation date/time information "\
62
61
  "specified.")
@@ -63,11 +63,8 @@ module RTF
63
63
  # This class represents a specialisation of the Node class to refer to a Node
64
64
  # that simply contains text.
65
65
  class TextNode < Node
66
- # Attribute accessor.
67
- attr_reader :text
68
-
69
- # Attribute mutator.
70
- attr_writer :text
66
+ # Actual text
67
+ attr_accessor :text
71
68
 
72
69
  # This is the constructor for the TextNode class.
73
70
  #
@@ -131,11 +128,8 @@ module RTF
131
128
  class ContainerNode < Node
132
129
  include Enumerable
133
130
 
134
- # Attribute accessor.
135
- attr_reader :children
136
-
137
- # Attribute mutator.
138
- attr_writer :children
131
+ # Children elements of the node
132
+ attr_accessor :children
139
133
 
140
134
  # This is the constructor for the ContainerNode class.
141
135
  #
@@ -208,11 +202,14 @@ module RTF
208
202
  # is concrete enough to be used on its own but will also be used as the
209
203
  # base class for some specific command node types.
210
204
  class CommandNode < ContainerNode
211
- # Attribute accessor.
212
- attr_reader :prefix, :suffix, :split
213
-
214
- # Attribute mutator.
215
- attr_writer :prefix, :suffix, :split
205
+ # String containing the prefix text for the command
206
+ attr_accessor :prefix
207
+ # String containing the suffix text for the command
208
+ attr_accessor :suffix
209
+ # A boolean to indicate whether the prefix and suffix should
210
+ # be written to separate lines whether the node is converted
211
+ # to RTF. Defaults to true
212
+ attr_accessor :split
216
213
 
217
214
  # This is the constructor for the CommandNode class.
218
215
  #
@@ -306,6 +303,19 @@ module RTF
306
303
  end
307
304
  end
308
305
 
306
+ # This method inserts a new image at the current position in a node.
307
+ #
308
+ # ==== Parameters
309
+ # source:: Either a string containing the path and name of a file or a
310
+ # File object for the image file to be inserted.
311
+ #
312
+ # ==== Exceptions
313
+ # RTFError:: Generated whenever an invalid or inaccessible file is
314
+ # specified or the image file type is not supported.
315
+ def image(source)
316
+ self.store(ImageNode.new(self, source, root.get_id))
317
+ end
318
+
309
319
  # This method provides a short cut means for applying multiple styles via
310
320
  # single command node. The method accepts a block that will be passed a
311
321
  # reference to the node created. Once the block is complete the new node
@@ -509,12 +519,9 @@ module RTF
509
519
  # specialised container nodes that contain only TableRowNodes and have their
510
520
  # size specified when they are created an cannot be resized after that.
511
521
  class TableNode < ContainerNode
512
- # Attribute accessor.
513
- attr_reader :cell_margin
514
-
515
- # Attribute mutator.
516
- attr_writer :cell_margin
517
-
522
+ # Cell margin. Default to 100
523
+ attr_accessor :cell_margin
524
+
518
525
  # This is a constructor for the TableNode class.
519
526
  #
520
527
  # ==== Parameters
@@ -523,12 +530,22 @@ module RTF
523
530
  # columns:: The number of columns in the table.
524
531
  # *widths:: One or more integers specifying the widths of the table
525
532
  # columns.
526
- def initialize(parent, rows, columns, *widths)
533
+ def initialize(parent, *args, &block)
534
+ if args.size>=2
535
+ rows=args.shift
536
+ columns=args.shift
537
+ widths=args
527
538
  super(parent) do
528
539
  entries = []
529
540
  rows.times {entries.push(TableRowNode.new(self, columns, *widths))}
530
541
  entries
531
542
  end
543
+
544
+ elsif block
545
+ block.arity<1 ? self.instance_eval(&block) : block.call(self)
546
+ else
547
+ raise "You should use 0 or >2 args"
548
+ end
532
549
  @cell_margin = 100
533
550
  end
534
551
 
@@ -656,7 +673,7 @@ module RTF
656
673
  end
657
674
  end
658
675
 
659
- # Attrobute accessors
676
+ # Attribute accessors
660
677
  def length
661
678
  entries.size
662
679
  end
@@ -732,13 +749,11 @@ module RTF
732
749
  class TableCellNode < CommandNode
733
750
  # A definition for the default width for the cell.
734
751
  DEFAULT_WIDTH = 300
735
-
752
+ # Width of cell
753
+ attr_accessor :width
736
754
  # Attribute accessor.
737
- attr_reader :width, :shading_colour, :style
738
-
739
- # Attribute mutator.
740
- attr_writer :width, :style
741
-
755
+ attr_reader :shading_colour, :style
756
+
742
757
  # This is the constructor for the TableCellNode class.
743
758
  #
744
759
  # ==== Parameters
@@ -1058,6 +1073,248 @@ module RTF
1058
1073
  end # End of the FooterNode class.
1059
1074
 
1060
1075
 
1076
+ # This class represents an image within a RTF document. Currently only the
1077
+ # PNG, JPEG and Windows Bitmap formats are supported. Efforts are made to
1078
+ # identify the file type but these are not guaranteed to work.
1079
+ class ImageNode < Node
1080
+ # A definition for an image type constant.
1081
+ PNG = :pngblip
1082
+
1083
+ # A definition for an image type constant.
1084
+ JPEG = :jpegblip
1085
+
1086
+ # A definition for an image type constant.
1087
+ BITMAP = :dibitmap0
1088
+
1089
+ # A definition for an architecture endian constant.
1090
+ LITTLE_ENDIAN = :little
1091
+
1092
+ # A definition for an architecture endian constant.
1093
+ BIG_ENDIAN = :big
1094
+
1095
+ # Attribute accessor.
1096
+ attr_reader :x_scaling, :y_scaling, :top_crop, :right_crop, :bottom_crop,
1097
+ :left_crop, :width, :height
1098
+
1099
+ # Attribute mutator.
1100
+ attr_writer :x_scaling, :y_scaling, :top_crop, :right_crop, :bottom_crop,
1101
+ :left_crop
1102
+
1103
+
1104
+ # This is the constructor for the ImageNode class.
1105
+ #
1106
+ # ==== Parameters
1107
+ # parent:: A reference to the node that owns the new image node.
1108
+ # source:: A reference to the image source. This must be a String or a
1109
+ # File.
1110
+ # id:: The unique identifier for the image node.
1111
+ #
1112
+ # ==== Exceptions
1113
+ # RTFError:: Generated whenever the image specified is not recognised as
1114
+ # a supported image type, something other than a String or
1115
+ # File or IO is passed as the source parameter or if the
1116
+ # specified source does not exist or cannot be accessed.
1117
+ def initialize(parent, source, id)
1118
+ super(parent)
1119
+ @source = nil
1120
+ @id = id
1121
+ @read = []
1122
+ @type = nil
1123
+ @x_scaling = @y_scaling = nil
1124
+ @top_crop = @right_crop = @bottom_crop = @left_crop = nil
1125
+ @width = @height = nil
1126
+
1127
+ # Check what we were given.
1128
+ src = source
1129
+ src.binmode if src.instance_of?(File)
1130
+ src = File.new(source, 'rb') if source.instance_of?(String)
1131
+ if src.instance_of?(File)
1132
+ # Check the files existence and accessibility.
1133
+ if !File.exist?(src.path)
1134
+ RTFError.fire("Unable to find the #{File.basename(source)} file.")
1135
+ end
1136
+ if !File.readable?(src.path)
1137
+ RTFError.fire("Access to the #{File.basename(source)} file denied.")
1138
+ end
1139
+ @source = src
1140
+ else
1141
+ RTFError.fire("Unrecognised source specified for ImageNode.")
1142
+ end
1143
+
1144
+ @type = get_file_type(src)
1145
+ if @type == nil
1146
+ RTFError.fire("The #{File.basename(source)} file contains an "\
1147
+ "unknown or unsupported image type.")
1148
+ end
1149
+
1150
+ @width, @height = get_dimensions
1151
+ end
1152
+
1153
+ # This method attempts to determine the image type associated with a
1154
+ # file, returning nil if it fails to make the determination.
1155
+ #
1156
+ # ==== Parameters
1157
+ # file:: A reference to the file to check for image type.
1158
+ def get_file_type(file)
1159
+ type = nil
1160
+
1161
+ # Check if the file is a JPEG.
1162
+ read_source(2)
1163
+
1164
+ if @read[0,2] == [255, 216]
1165
+ type = JPEG
1166
+ else
1167
+ # Check if it's a PNG.
1168
+ read_source(6)
1169
+ if @read[0,8] == [137, 80, 78, 71, 13, 10, 26, 10]
1170
+ type = PNG
1171
+ else
1172
+ # Check if its a bitmap.
1173
+ if @read[0,2] == [66, 77]
1174
+ size = to_integer(@read[2,4])
1175
+ type = BITMAP if size == File.size(file.path)
1176
+ end
1177
+ end
1178
+ end
1179
+
1180
+ type
1181
+ end
1182
+
1183
+ # This method generates the RTF for an ImageNode object.
1184
+ def to_rtf
1185
+ text = StringIO.new
1186
+ count = 0
1187
+
1188
+ #text << '{\pard{\*\shppict{\pict'
1189
+ text << '{\*\shppict{\pict'
1190
+ text << "\\picscalex#{@x_scaling}" if @x_scaling != nil
1191
+ text << "\\picscaley#{@y_scaling}" if @y_scaling != nil
1192
+ text << "\\piccropl#{@left_crop}" if @left_crop != nil
1193
+ text << "\\piccropr#{@right_crop}" if @right_crop != nil
1194
+ text << "\\piccropt#{@top_crop}" if @top_crop != nil
1195
+ text << "\\piccropb#{@bottom_crop}" if @bottom_crop != nil
1196
+ text << "\\picw#{@width}\\pich#{@height}\\bliptag#{@id}"
1197
+ text << "\\#{@type.id2name}\n"
1198
+ @source.each_byte {|byte| @read << byte} if @source.eof? == false
1199
+ @read.each do |byte|
1200
+ text << ("%02x" % byte)
1201
+ count += 1
1202
+ if count == 40
1203
+ text << "\n"
1204
+ count = 0
1205
+ end
1206
+ end
1207
+ #text << "\n}}\\par}"
1208
+ text << "\n}}"
1209
+
1210
+ text.string
1211
+ end
1212
+
1213
+ # This method is used to determine the underlying endianness of a
1214
+ # platform.
1215
+ def get_endian
1216
+ [0, 125].pack('c2').unpack('s') == [125] ? BIG_ENDIAN : LITTLE_ENDIAN
1217
+ end
1218
+
1219
+ # This method converts an array to an integer. The array must be either
1220
+ # two or four bytes in length.
1221
+ #
1222
+ # ==== Parameters
1223
+ # array:: A reference to the array containing the data to be converted.
1224
+ # signed:: A boolean to indicate whether the value is signed. Defaults
1225
+ # to false.
1226
+ def to_integer(array, signed=false)
1227
+ from = nil
1228
+ to = nil
1229
+ data = []
1230
+
1231
+ if array.size == 2
1232
+ data.concat(get_endian == BIG_ENDIAN ? array.reverse : array)
1233
+ from = 'C2'
1234
+ to = signed ? 's' : 'S'
1235
+ else
1236
+ data.concat(get_endian == BIG_ENDIAN ? array[0,4].reverse : array)
1237
+ from = 'C4'
1238
+ to = signed ? 'l' : 'L'
1239
+ end
1240
+ data.pack(from).unpack(to)[0]
1241
+ end
1242
+
1243
+ # This method loads the data for an image from its source. The method
1244
+ # accepts two call approaches. If called without a block then the method
1245
+ # considers the size parameter it is passed. If called with a block the
1246
+ # method executes until the block returns true.
1247
+ #
1248
+ # ==== Parameters
1249
+ # size:: The maximum number of bytes to be read from the file. Defaults
1250
+ # to nil to indicate that the remainder of the file should be read
1251
+ # in.
1252
+ def read_source(size=nil)
1253
+ if block_given?
1254
+ done = false
1255
+
1256
+ while done == false && @source.eof? == false
1257
+ @read << @source.getbyte
1258
+ done = yield @read[-1]
1259
+ end
1260
+ else
1261
+ if size != nil
1262
+ if size > 0
1263
+ total = 0
1264
+ while @source.eof? == false && total < size
1265
+
1266
+ @read << @source.getbyte
1267
+ total += 1
1268
+ end
1269
+ end
1270
+ else
1271
+ @source.each_byte {|byte| @read << byte}
1272
+ end
1273
+ end
1274
+ end
1275
+
1276
+ # This method fetches details of the dimensions associated with an image.
1277
+ def get_dimensions
1278
+ dimensions = nil
1279
+
1280
+ # Check the image type.
1281
+ if @type == JPEG
1282
+ # Read until we can't anymore or we've found what we're looking for.
1283
+ done = false
1284
+ while @source.eof? == false && done == false
1285
+ # Read to the next marker.
1286
+ read_source {|c| c == 0xff} # Read to the marker.
1287
+ read_source {|c| c != 0xff} # Skip any padding.
1288
+
1289
+ if @read[-1] >= 0xc0 && @read[-1] <= 0xc3
1290
+ # Read in the width and height details.
1291
+ read_source(7)
1292
+ dimensions = @read[-4,4].pack('C4').unpack('nn').reverse
1293
+ done = true
1294
+ else
1295
+ # Skip the marker block.
1296
+ read_source(2)
1297
+ read_source(@read[-2,2].pack('C2').unpack('n')[0] - 2)
1298
+ end
1299
+ end
1300
+ elsif @type == PNG
1301
+ # Read in the data to contain the width and height.
1302
+ read_source(16)
1303
+ dimensions = @read[-8,8].pack('C8').unpack('N2')
1304
+ elsif @type == BITMAP
1305
+ # Read in the data to contain the width and height.
1306
+ read_source(18)
1307
+ dimensions = [to_integer(@read[-8,4]), to_integer(@read[-4,4])]
1308
+ end
1309
+
1310
+ dimensions
1311
+ end
1312
+
1313
+ private :read_source, :get_file_type, :to_integer, :get_endian,
1314
+ :get_dimensions
1315
+ end # End of the ImageNode class.
1316
+
1317
+
1061
1318
  # This class represents an RTF document. In actuality it is just a
1062
1319
  # specialised Node type that cannot be assigned a parent and that holds
1063
1320
  # document font, colour and information tables.
@@ -1235,6 +1492,14 @@ module RTF
1235
1492
  @style = style == nil ? DocumentStyle.new : style
1236
1493
  @headers = [nil, nil, nil, nil]
1237
1494
  @footers = [nil, nil, nil, nil]
1495
+ @id = 0
1496
+ end
1497
+
1498
+ # This method provides a method that can be called to generate an
1499
+ # identifier that is unique within the document.
1500
+ def get_id
1501
+ @id += 1
1502
+ Time.now().strftime('%d%m%y') + @id.to_s
1238
1503
  end
1239
1504
 
1240
1505
  # Attribute accessor.