clbustos-rtf 0.4.2 → 0.5.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.
- data/.gemtest +0 -0
- data/History.txt +53 -0
- data/{LICENSE → LICENSE.txt} +0 -0
- data/Manifest.txt +52 -0
- data/README.rdoc +60 -63
- data/README.txt +201 -0
- data/Rakefile +18 -34
- data/examples/example01.rb +5 -1
- data/examples/example02.rb +1 -0
- data/examples/example03.rb +1 -0
- data/examples/example03.rtf +0 -0
- data/examples/example04.rb +1 -0
- data/examples/rubyrtf.bmp +0 -0
- data/examples/rubyrtf.jpg +0 -0
- data/examples/rubyrtf.png +0 -0
- data/ifad-rtf.gemspec +120 -0
- data/lib/rtf.rb +1 -0
- data/lib/rtf/colour.rb +0 -0
- data/lib/rtf/converters.rb +5 -0
- data/lib/rtf/converters/html.rb +123 -0
- data/lib/rtf/font.rb +0 -0
- data/lib/rtf/information.rb +0 -0
- data/lib/rtf/node.rb +136 -109
- data/lib/rtf/paper.rb +0 -0
- data/lib/rtf/style.rb +0 -0
- data/test/fixtures/bitmap1.bmp +0 -0
- data/test/fixtures/bitmap2.bmp +0 -0
- data/test/fixtures/jpeg1.jpg +0 -0
- data/test/fixtures/jpeg2.jpg +0 -0
- data/test/fixtures/png1.png +0 -0
- data/test/fixtures/png2.png +0 -0
- data/test/{test_helper.rb → helper_tests.rb} +1 -0
- data/test/{character_style_test.rb → test_character_style.rb} +1 -1
- data/test/{colour_test.rb → test_colour.rb} +2 -2
- data/test/{colour_table_test.rb → test_colour_table.rb} +2 -2
- data/test/{command_node_test.rb → test_command_node.rb} +1 -1
- data/test/{container_node_test.rb → test_container_node.rb} +2 -1
- data/test/{document_test.rb → test_document.rb} +1 -1
- data/test/{document_style_test.rb → test_document_style.rb} +1 -1
- data/test/{font_test.rb → test_font.rb} +2 -2
- data/test/{font_table_test.rb → test_font_table.rb} +2 -2
- data/test/{footer_node_test.rb → test_footer_node.rb} +1 -1
- data/test/{header_node_test.rb → test_header_node.rb} +1 -1
- data/test/{image_node_test.rb → test_image_node.rb} +10 -3
- data/test/{information_test.rb → test_information.rb} +1 -1
- data/test/{node_test.rb → test_node.rb} +1 -1
- data/test/{paragraph_style_test.rb → test_paragraph_style.rb} +1 -1
- data/test/{style_test.rb → test_style.rb} +1 -1
- data/test/{table_cell_node_test.rb → test_table_cell_node.rb} +1 -1
- data/test/{table_node_test.rb → test_table_node.rb} +2 -1
- data/test/{table_row_node_test.rb → test_table_row_node.rb} +1 -1
- data/test/text_node_test.rb +35 -10
- metadata +137 -97
- data/CHANGES +0 -23
- data/VERSION.yml +0 -5
data/Rakefile
CHANGED
@@ -1,40 +1,26 @@
|
|
1
|
+
$:.unshift(File.expand_path(File.dirname(__FILE__)+"/lib"))
|
1
2
|
$:.unshift(File.expand_path(File.dirname(__FILE__)))
|
2
3
|
require 'rake'
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
s.name = "clbustos-rtf"
|
9
|
-
s.summary = 'Ruby library to create rich text format documents.'
|
10
|
-
s.email = "clbustos@gmail.com"
|
11
|
-
s.homepage = "http://github.com/clbustos/rtf"
|
12
|
-
s.description = 'Ruby RTF is a library that can be used to create '\
|
13
|
-
'rich text format (RTF) documents. RTF is a text '\
|
14
|
-
'based standard for laying out document content.'
|
15
|
-
s.authors = ["Peter Wood"]
|
16
|
-
s.files = FileList["[A-Z]*", "{examples,lib,test}/**/*"]
|
17
|
-
end
|
18
|
-
rescue LoadError
|
19
|
-
puts "Jeweler not available. Install it with: sudo gem install jeweler"
|
20
|
-
end
|
5
|
+
require 'rubygems'
|
6
|
+
require 'hoe'
|
7
|
+
require 'rtf'
|
8
|
+
Hoe.plugin :git
|
21
9
|
|
22
|
-
require 'rake/rdoctask'
|
23
|
-
Rake::RDocTask.new do |rdoc|
|
24
|
-
rdoc.rdoc_dir = 'rdoc'
|
25
|
-
rdoc.title = 'ruby-rtf'
|
26
|
-
rdoc.options << '--line-numbers' << '--inline-source'
|
27
|
-
rdoc.rdoc_files.include('[A-Z]*')
|
28
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
29
|
-
end
|
30
10
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
11
|
+
h=Hoe.spec 'clbustos-rtf' do
|
12
|
+
# Original author: Peter Wood
|
13
|
+
self.developer 'Claudio Bustos', 'clbustos_at_gmail.com'
|
14
|
+
self.version=RTF::VERSION
|
15
|
+
self.git_log_author=true
|
16
|
+
path = File.expand_path("~/.rubyforge/user-config.yml")
|
17
|
+
config = YAML.load(File.read(path))
|
18
|
+
host = "#{config["username"]}@rubyforge.org"
|
19
|
+
|
20
|
+
remote_dir = "#{host}:/var/www/gforge-projects/ruby-statsample/rtf"
|
21
|
+
self.rdoc_locations << remote_dir
|
22
|
+
self.extra_dev_deps << ["hoe",">=0"]
|
36
23
|
end
|
37
|
-
|
38
24
|
begin
|
39
25
|
require 'rcov/rcovtask'
|
40
26
|
Rcov::RcovTask.new do |t|
|
@@ -43,7 +29,5 @@ begin
|
|
43
29
|
t.verbose = true
|
44
30
|
end
|
45
31
|
rescue LoadError
|
46
|
-
puts "RCov is not available. In order to run rcov, you must: sudo gem install
|
32
|
+
puts "RCov is not available. In order to run rcov, you must: sudo gem install rcov"
|
47
33
|
end
|
48
|
-
|
49
|
-
task :default => :test
|
data/examples/example01.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
#!/usr/bin/env ruby
|
2
3
|
|
4
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__)+"/../lib/")
|
5
|
+
|
6
|
+
|
3
7
|
require 'rubygems'
|
4
8
|
require 'rtf'
|
5
9
|
|
@@ -48,4 +52,4 @@ end
|
|
48
52
|
|
49
53
|
File.open('example01.rtf', 'w') do |file|
|
50
54
|
file.write(document.to_rtf)
|
51
|
-
end
|
55
|
+
end
|
data/examples/example02.rb
CHANGED
data/examples/example03.rb
CHANGED
data/examples/example03.rtf
CHANGED
File without changes
|
data/examples/example04.rb
CHANGED
data/examples/rubyrtf.bmp
CHANGED
File without changes
|
data/examples/rubyrtf.jpg
CHANGED
File without changes
|
data/examples/rubyrtf.png
CHANGED
File without changes
|
data/ifad-rtf.gemspec
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{ifad-rtf}
|
8
|
+
s.version = "0.4.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Peter Wood", "Claudio Bustos", "Marcello Barnaba"]
|
12
|
+
s.date = %q{2011-06-08}
|
13
|
+
s.description = %q{Ruby RTF is a library that can be used to create rich text format (RTF) documents. RTF is a text based standard for laying out document content.}
|
14
|
+
s.email = %q{m.barnaba@ifad.org}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"CHANGES",
|
21
|
+
"LICENSE",
|
22
|
+
"README.rdoc",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION.yml",
|
25
|
+
"examples/example01.rb",
|
26
|
+
"examples/example02.rb",
|
27
|
+
"examples/example03.rb",
|
28
|
+
"examples/example03.rtf",
|
29
|
+
"examples/example04.rb",
|
30
|
+
"examples/rubyrtf.bmp",
|
31
|
+
"examples/rubyrtf.jpg",
|
32
|
+
"examples/rubyrtf.png",
|
33
|
+
"lib/rtf.rb",
|
34
|
+
"lib/rtf/colour.rb",
|
35
|
+
"lib/rtf/converters.rb",
|
36
|
+
"lib/rtf/converters/html.rb",
|
37
|
+
"lib/rtf/font.rb",
|
38
|
+
"lib/rtf/information.rb",
|
39
|
+
"lib/rtf/list.rb",
|
40
|
+
"lib/rtf/node.rb",
|
41
|
+
"lib/rtf/paper.rb",
|
42
|
+
"lib/rtf/style.rb",
|
43
|
+
"test/character_style_test.rb",
|
44
|
+
"test/colour_table_test.rb",
|
45
|
+
"test/colour_test.rb",
|
46
|
+
"test/command_node_test.rb",
|
47
|
+
"test/container_node_test.rb",
|
48
|
+
"test/document_style_test.rb",
|
49
|
+
"test/document_test.rb",
|
50
|
+
"test/fixtures/bitmap1.bmp",
|
51
|
+
"test/fixtures/bitmap2.bmp",
|
52
|
+
"test/fixtures/jpeg1.jpg",
|
53
|
+
"test/fixtures/jpeg2.jpg",
|
54
|
+
"test/fixtures/png1.png",
|
55
|
+
"test/fixtures/png2.png",
|
56
|
+
"test/font_table_test.rb",
|
57
|
+
"test/font_test.rb",
|
58
|
+
"test/footer_node_test.rb",
|
59
|
+
"test/header_node_test.rb",
|
60
|
+
"test/image_node_test.rb",
|
61
|
+
"test/information_test.rb",
|
62
|
+
"test/node_test.rb",
|
63
|
+
"test/paragraph_style_test.rb",
|
64
|
+
"test/style_test.rb",
|
65
|
+
"test/table_cell_node_test.rb",
|
66
|
+
"test/table_node_test.rb",
|
67
|
+
"test/table_row_node_test.rb",
|
68
|
+
"test/test_helper.rb",
|
69
|
+
"test/text_node_test.rb"
|
70
|
+
]
|
71
|
+
s.homepage = %q{http://github.com/ifad/rtf}
|
72
|
+
s.require_paths = ["lib"]
|
73
|
+
s.rubyforge_project = %q{ruby-statsample}
|
74
|
+
s.rubygems_version = %q{1.3.7}
|
75
|
+
s.summary = %q{Ruby library to create rich text format documents.}
|
76
|
+
s.test_files = [
|
77
|
+
"examples/example01.rb",
|
78
|
+
"examples/example02.rb",
|
79
|
+
"examples/example03.rb",
|
80
|
+
"examples/example04.rb",
|
81
|
+
"test/character_style_test.rb",
|
82
|
+
"test/colour_table_test.rb",
|
83
|
+
"test/colour_test.rb",
|
84
|
+
"test/command_node_test.rb",
|
85
|
+
"test/container_node_test.rb",
|
86
|
+
"test/document_style_test.rb",
|
87
|
+
"test/document_test.rb",
|
88
|
+
"test/font_table_test.rb",
|
89
|
+
"test/font_test.rb",
|
90
|
+
"test/footer_node_test.rb",
|
91
|
+
"test/header_node_test.rb",
|
92
|
+
"test/image_node_test.rb",
|
93
|
+
"test/information_test.rb",
|
94
|
+
"test/node_test.rb",
|
95
|
+
"test/paragraph_style_test.rb",
|
96
|
+
"test/style_test.rb",
|
97
|
+
"test/table_cell_node_test.rb",
|
98
|
+
"test/table_node_test.rb",
|
99
|
+
"test/table_row_node_test.rb",
|
100
|
+
"test/test_helper.rb",
|
101
|
+
"test/text_node_test.rb"
|
102
|
+
]
|
103
|
+
|
104
|
+
if s.respond_to? :specification_version then
|
105
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
106
|
+
s.specification_version = 3
|
107
|
+
|
108
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
109
|
+
s.add_runtime_dependency(%q<nokogiri>, ["~> 1.1"])
|
110
|
+
s.add_runtime_dependency(%q<tidy-ext>, ["~> 0.1"])
|
111
|
+
else
|
112
|
+
s.add_dependency(%q<nokogiri>, ["~> 1.1"])
|
113
|
+
s.add_dependency(%q<tidy-ext>, ["~> 0.1"])
|
114
|
+
end
|
115
|
+
else
|
116
|
+
s.add_dependency(%q<nokogiri>, ["~> 1.1"])
|
117
|
+
s.add_dependency(%q<tidy-ext>, ["~> 0.1"])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
data/lib/rtf.rb
CHANGED
@@ -12,6 +12,7 @@ require 'rtf/list'
|
|
12
12
|
# This module encapsulates all the classes and definitions relating to the RTF
|
13
13
|
# library.
|
14
14
|
module RTF
|
15
|
+
VERSION="0.5.0"
|
15
16
|
# This is the exception class used by the RTF library code to indicate
|
16
17
|
# errors.
|
17
18
|
class RTFError < StandardError
|
data/lib/rtf/colour.rb
CHANGED
File without changes
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'tidy'
|
3
|
+
|
4
|
+
module RTF::Converters
|
5
|
+
class HTML
|
6
|
+
|
7
|
+
def initialize(html, options = {})
|
8
|
+
html = options[:noclean] ? html : clean(html, options[:tidy_options] || {})
|
9
|
+
@html = Nokogiri::HTML::Document.parse(html)
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_rtf(options = {})
|
13
|
+
to_rtf_document(options).to_rtf
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_rtf_document(options = {})
|
17
|
+
font = Helpers.font(options[:font] || :default)
|
18
|
+
nodes = NodeSet.new @html.css('body').children
|
19
|
+
|
20
|
+
RTF::Document.new(font).tap do |rtf|
|
21
|
+
nodes.to_rtf(rtf)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
def clean(html, options = {})
|
27
|
+
defaults = {
|
28
|
+
:doctype => 'omit',
|
29
|
+
:bare => true,
|
30
|
+
:clean => true,
|
31
|
+
:drop_empty_paras => true,
|
32
|
+
:logical_emphasis => true,
|
33
|
+
:lower_literals => true,
|
34
|
+
:merge_spans => 1,
|
35
|
+
:merge_divs => 1,
|
36
|
+
:output_html => true,
|
37
|
+
:indent => 0,
|
38
|
+
:wrap => 0,
|
39
|
+
:char_encoding => 'utf8'
|
40
|
+
}
|
41
|
+
|
42
|
+
tidy = Tidy.new defaults.merge(options)
|
43
|
+
tidy.clean(html)
|
44
|
+
end
|
45
|
+
|
46
|
+
module Helpers
|
47
|
+
extend self
|
48
|
+
|
49
|
+
def font(key)
|
50
|
+
RTF::Font.new(*case key
|
51
|
+
when :default then [RTF::Font::ROMAN, 'Times New Roman']
|
52
|
+
when :monospace then [RTF::Font::MODERN, 'Courier New' ]
|
53
|
+
end)
|
54
|
+
end
|
55
|
+
|
56
|
+
def style(key)
|
57
|
+
RTF::CharacterStyle.new.tap do |style|
|
58
|
+
case key.to_sym
|
59
|
+
when :h1
|
60
|
+
style.font_size = 44
|
61
|
+
style.bold = true
|
62
|
+
when :h2
|
63
|
+
style.font_size = 36
|
64
|
+
style.bold = true
|
65
|
+
when :h3
|
66
|
+
style.font_size = 28
|
67
|
+
style.bold = true
|
68
|
+
when :h4
|
69
|
+
style.font_size = 22
|
70
|
+
style.bold = true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class NodeSet
|
77
|
+
def initialize(nodeset)
|
78
|
+
@nodeset = nodeset
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_rtf(rtf)
|
82
|
+
@nodeset.each do |node|
|
83
|
+
Node.new(node).to_rtf(rtf)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Node # :nodoc:
|
89
|
+
def initialize(node)
|
90
|
+
@node = node
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_rtf(rtf)
|
94
|
+
case @node.name
|
95
|
+
when 'text' then rtf << @node.text.gsub(/\n+/, ' ').strip
|
96
|
+
when 'br' then rtf.line_break
|
97
|
+
when 'b', 'strong' then rtf.bold &recurse
|
98
|
+
when 'i', 'em', 'cite' then rtf.italic &recurse
|
99
|
+
when 'u' then rtf.underline &recurse
|
100
|
+
when 'blockquote', 'p', 'div' then rtf.paragraph &recurse
|
101
|
+
when 'span' then recurse.call(rtf)
|
102
|
+
when 'sup' then rtf.subscript &recurse
|
103
|
+
when 'sub' then rtf.superscript &recurse
|
104
|
+
when 'ul' then rtf.list :bullets, &recurse
|
105
|
+
when 'ol' then rtf.list :decimal, &recurse
|
106
|
+
when 'li' then rtf.item &recurse
|
107
|
+
when 'a' then rtf.link @node[:href], &recurse
|
108
|
+
when 'h1', 'h2', 'h3', 'h4' then rtf.apply(Helpers.style(@node.name), &recurse); rtf.line_break
|
109
|
+
when 'code' then rtf.font Helpers.font(:monospace), &recurse
|
110
|
+
else
|
111
|
+
#puts "Ignoring #{@node.to_html}"
|
112
|
+
end
|
113
|
+
|
114
|
+
return rtf
|
115
|
+
end
|
116
|
+
|
117
|
+
def recurse
|
118
|
+
lambda {|rtf| NodeSet.new(@node.children).to_rtf(rtf)}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
data/lib/rtf/font.rb
CHANGED
File without changes
|
data/lib/rtf/information.rb
CHANGED
File without changes
|
data/lib/rtf/node.rb
CHANGED
@@ -115,13 +115,20 @@ module RTF
|
|
115
115
|
# method escapes any special sequences that appear in the text.
|
116
116
|
def to_rtf
|
117
117
|
rtf=(@text == nil ? '' : @text.gsub("{", "\\{").gsub("}", "\\}").gsub("\\", "\\\\"))
|
118
|
+
# This is from lfarcy / rtf-extensions
|
119
|
+
# I don't see the point of coding different 128<n<256 range
|
120
|
+
|
121
|
+
#f1=lambda { |n| n < 128 ? n.chr : n < 256 ? "\\'#{n.to_s(16)}" : "\\u#{n}\\'3f" }
|
118
122
|
# Encode as Unicode.
|
123
|
+
|
124
|
+
f=lambda { |n| n < 128 ? n.chr : "\\u#{n}\\'3f" }
|
125
|
+
# Ruby 1.9 is safe, cause detect original encoding
|
126
|
+
# and convert text to utf-16 first
|
119
127
|
if RUBY_VERSION>"1.9.0"
|
120
|
-
rtf.encode("UTF-16LE").each_codepoint.map
|
121
|
-
cp < 128 ? cp.chr : "\\u#{cp}\\'3f"
|
122
|
-
}.join("")
|
128
|
+
return rtf.encode("UTF-16LE", :undef=>:replace).each_codepoint.map(&f).join('')
|
123
129
|
else
|
124
|
-
|
130
|
+
# You SHOULD use UTF-8 as input, ok?
|
131
|
+
return rtf.unpack('U*').map(&f).join('')
|
125
132
|
end
|
126
133
|
end
|
127
134
|
end # End of the TextNode class.
|
@@ -701,7 +708,7 @@ module RTF
|
|
701
708
|
class TableNode < ContainerNode
|
702
709
|
# Cell margin. Default to 100
|
703
710
|
attr_accessor :cell_margin
|
704
|
-
|
711
|
+
|
705
712
|
# This is a constructor for the TableNode class.
|
706
713
|
#
|
707
714
|
# ==== Parameters
|
@@ -720,7 +727,7 @@ module RTF
|
|
720
727
|
rows.times {entries.push(TableRowNode.new(self, columns, *widths))}
|
721
728
|
entries
|
722
729
|
end
|
723
|
-
|
730
|
+
|
724
731
|
elsif block
|
725
732
|
block.arity<1 ? self.instance_eval(&block) : block.call(self)
|
726
733
|
else
|
@@ -822,7 +829,7 @@ module RTF
|
|
822
829
|
text << row.to_rtf
|
823
830
|
end
|
824
831
|
|
825
|
-
text.string
|
832
|
+
text.string.sub(/\\row(?!.*\\row)/m, "\\lastrow\n\\row")
|
826
833
|
end
|
827
834
|
|
828
835
|
alias :column_shading_color :column_shading_colour
|
@@ -933,7 +940,7 @@ module RTF
|
|
933
940
|
attr_accessor :width
|
934
941
|
# Attribute accessor.
|
935
942
|
attr_reader :shading_colour, :style
|
936
|
-
|
943
|
+
|
937
944
|
# This is the constructor for the TableCellNode class.
|
938
945
|
#
|
939
946
|
# ==== Parameters
|
@@ -1261,13 +1268,20 @@ module RTF
|
|
1261
1268
|
# A definition for an architecture endian constant.
|
1262
1269
|
BIG_ENDIAN = :big
|
1263
1270
|
|
1271
|
+
# Offsets for reading dimension data by filetype
|
1272
|
+
DIMENSIONS_OFFSET = {
|
1273
|
+
JPEG => 2,
|
1274
|
+
PNG => 8,
|
1275
|
+
BITMAP => 8,
|
1276
|
+
}.freeze
|
1277
|
+
|
1264
1278
|
# Attribute accessor.
|
1265
1279
|
attr_reader :x_scaling, :y_scaling, :top_crop, :right_crop, :bottom_crop,
|
1266
|
-
:left_crop, :width, :height
|
1280
|
+
:left_crop, :width, :height, :displayed_width, :displayed_height
|
1267
1281
|
|
1268
1282
|
# Attribute mutator.
|
1269
1283
|
attr_writer :x_scaling, :y_scaling, :top_crop, :right_crop, :bottom_crop,
|
1270
|
-
:left_crop
|
1284
|
+
:left_crop, :displayed_width, :displayed_height
|
1271
1285
|
|
1272
1286
|
|
1273
1287
|
# This is the constructor for the ImageNode class.
|
@@ -1287,63 +1301,66 @@ module RTF
|
|
1287
1301
|
super(parent)
|
1288
1302
|
@source = nil
|
1289
1303
|
@id = id
|
1290
|
-
@read = []
|
1291
1304
|
@type = nil
|
1292
1305
|
@x_scaling = @y_scaling = nil
|
1293
1306
|
@top_crop = @right_crop = @bottom_crop = @left_crop = nil
|
1294
1307
|
@width = @height = nil
|
1308
|
+
@displayed_width = @displayed_height = nil
|
1295
1309
|
|
1296
|
-
#
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
RTFError.fire("Access to the #{File.basename(source)} file denied.")
|
1307
|
-
end
|
1308
|
-
@source = src
|
1309
|
-
else
|
1310
|
-
RTFError.fire("Unrecognised source specified for ImageNode.")
|
1310
|
+
# store path to image
|
1311
|
+
@source = source if source.instance_of?(String) || source.instance_of?(Tempfile)
|
1312
|
+
@source = source.path if source.instance_of?(File)
|
1313
|
+
|
1314
|
+
# Check the file's existence and accessibility.
|
1315
|
+
if !File.exist?(@source)
|
1316
|
+
RTFError.fire("Unable to find the #{File.basename(@source)} file.")
|
1317
|
+
end
|
1318
|
+
if !File.readable?(@source)
|
1319
|
+
RTFError.fire("Access to the #{File.basename(@source)} file denied.")
|
1311
1320
|
end
|
1312
1321
|
|
1313
|
-
@type = get_file_type
|
1322
|
+
@type = get_file_type
|
1314
1323
|
if @type == nil
|
1315
|
-
RTFError.fire("The #{File.basename(source)} file contains an "\
|
1324
|
+
RTFError.fire("The #{File.basename(@source)} file contains an "\
|
1316
1325
|
"unknown or unsupported image type.")
|
1317
1326
|
end
|
1318
1327
|
|
1319
1328
|
@width, @height = get_dimensions
|
1320
1329
|
end
|
1321
1330
|
|
1331
|
+
def open_file(&block)
|
1332
|
+
if block
|
1333
|
+
File.open(@source, 'rb', &block)
|
1334
|
+
else
|
1335
|
+
File.open(@source, 'rb')
|
1336
|
+
end
|
1337
|
+
end
|
1338
|
+
|
1322
1339
|
# This method attempts to determine the image type associated with a
|
1323
1340
|
# file, returning nil if it fails to make the determination.
|
1324
|
-
|
1325
|
-
# ==== Parameters
|
1326
|
-
# file:: A reference to the file to check for image type.
|
1327
|
-
def get_file_type(file)
|
1341
|
+
def get_file_type
|
1328
1342
|
type = nil
|
1343
|
+
read = []
|
1344
|
+
open_file do |file|
|
1345
|
+
|
1346
|
+
# Check if the file is a JPEG.
|
1347
|
+
read_source(file, read, 2)
|
1348
|
+
if read[0,2] == [255, 216]
|
1349
|
+
type = JPEG
|
1350
|
+
else
|
1351
|
+
# Check if it's a PNG.
|
1352
|
+
read_source(file, read, 6)
|
1353
|
+
if read[0,8] == [137, 80, 78, 71, 13, 10, 26, 10]
|
1354
|
+
type = PNG
|
1355
|
+
else
|
1356
|
+
# Check if its a bitmap.
|
1357
|
+
if read[0,2] == [66, 77]
|
1358
|
+
size = to_integer(read[2,4])
|
1359
|
+
type = BITMAP if size == File.size(@source)
|
1360
|
+
end
|
1361
|
+
end
|
1362
|
+
end
|
1329
1363
|
|
1330
|
-
# Check if the file is a JPEG.
|
1331
|
-
read_source(2)
|
1332
|
-
|
1333
|
-
if @read[0,2] == [255, 216]
|
1334
|
-
type = JPEG
|
1335
|
-
else
|
1336
|
-
# Check if it's a PNG.
|
1337
|
-
read_source(6)
|
1338
|
-
if @read[0,8] == [137, 80, 78, 71, 13, 10, 26, 10]
|
1339
|
-
type = PNG
|
1340
|
-
else
|
1341
|
-
# Check if its a bitmap.
|
1342
|
-
if @read[0,2] == [66, 77]
|
1343
|
-
size = to_integer(@read[2,4])
|
1344
|
-
type = BITMAP if size == File.size(file.path)
|
1345
|
-
end
|
1346
|
-
end
|
1347
1364
|
end
|
1348
1365
|
|
1349
1366
|
type
|
@@ -1351,32 +1368,38 @@ module RTF
|
|
1351
1368
|
|
1352
1369
|
# This method generates the RTF for an ImageNode object.
|
1353
1370
|
def to_rtf
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1371
|
+
text = StringIO.new
|
1372
|
+
count = 0
|
1373
|
+
|
1374
|
+
#text << '{\pard{\*\shppict{\pict'
|
1375
|
+
text << '{\*\shppict{\pict'
|
1376
|
+
text << "\\picscalex#{@x_scaling}" if @x_scaling != nil
|
1377
|
+
text << "\\picscaley#{@y_scaling}" if @y_scaling != nil
|
1378
|
+
text << "\\piccropl#{@left_crop}" if @left_crop != nil
|
1379
|
+
text << "\\piccropr#{@right_crop}" if @right_crop != nil
|
1380
|
+
text << "\\piccropt#{@top_crop}" if @top_crop != nil
|
1381
|
+
text << "\\piccropb#{@bottom_crop}" if @bottom_crop != nil
|
1382
|
+
text << "\\picwgoal#{@displayed_width}" if @displayed_width != nil
|
1383
|
+
text << "\\pichgoal#{@displayed_height}" if @displayed_height != nil
|
1384
|
+
text << "\\picw#{@width}\\pich#{@height}\\bliptag#{@id}"
|
1385
|
+
text << "\\#{@type.id2name}\n"
|
1386
|
+
|
1387
|
+
open_file do |file|
|
1388
|
+
file.each_byte do |byte|
|
1389
|
+
hex_str = byte.to_s(16)
|
1390
|
+
hex_str.insert(0,'0') if hex_str.length == 1
|
1391
|
+
text << hex_str
|
1370
1392
|
count += 1
|
1371
1393
|
if count == 40
|
1372
|
-
|
1373
|
-
|
1394
|
+
text << "\n"
|
1395
|
+
count = 0
|
1374
1396
|
end
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1397
|
+
end
|
1398
|
+
end
|
1399
|
+
#text << "\n}}\\par}"
|
1400
|
+
text << "\n}}"
|
1378
1401
|
|
1379
|
-
|
1402
|
+
text.string
|
1380
1403
|
end
|
1381
1404
|
|
1382
1405
|
# This method is used to determine the underlying endianness of a
|
@@ -1418,69 +1441,73 @@ module RTF
|
|
1418
1441
|
# size:: The maximum number of bytes to be read from the file. Defaults
|
1419
1442
|
# to nil to indicate that the remainder of the file should be read
|
1420
1443
|
# in.
|
1421
|
-
def read_source(size=nil)
|
1444
|
+
def read_source(file, read, size=nil)
|
1422
1445
|
if block_given?
|
1423
1446
|
done = false
|
1424
1447
|
|
1425
|
-
while done == false &&
|
1426
|
-
|
1427
|
-
done = yield
|
1448
|
+
while done == false && file.eof? == false
|
1449
|
+
read << file.getbyte
|
1450
|
+
done = yield read[-1]
|
1428
1451
|
end
|
1429
1452
|
else
|
1430
1453
|
if size != nil
|
1431
1454
|
if size > 0
|
1432
1455
|
total = 0
|
1433
|
-
while
|
1434
|
-
|
1435
|
-
@read << @source.getbyte
|
1456
|
+
while file.eof? == false && total < size
|
1457
|
+
read << file.getbyte
|
1436
1458
|
total += 1
|
1437
1459
|
end
|
1438
1460
|
end
|
1439
1461
|
else
|
1440
|
-
|
1462
|
+
file.each_byte {|byte| read << byte}
|
1441
1463
|
end
|
1442
1464
|
end
|
1443
1465
|
end
|
1444
1466
|
|
1467
|
+
|
1445
1468
|
# This method fetches details of the dimensions associated with an image.
|
1446
1469
|
def get_dimensions
|
1447
1470
|
dimensions = nil
|
1448
1471
|
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1472
|
+
open_file do |file|
|
1473
|
+
file.pos = DIMENSIONS_OFFSET[@type]
|
1474
|
+
read = []
|
1475
|
+
|
1476
|
+
# Check the image type.
|
1477
|
+
if @type == JPEG
|
1478
|
+
# Read until we can't anymore or we've found what we're looking for.
|
1479
|
+
done = false
|
1480
|
+
while file.eof? == false && done == false
|
1481
|
+
# Read to the next marker.
|
1482
|
+
read_source(file,read) {|c| c == 0xff} # Read to the marker.
|
1483
|
+
read_source(file,read) {|c| c != 0xff} # Skip any padding.
|
1484
|
+
|
1485
|
+
if read[-1] >= 0xc0 && read[-1] <= 0xc3
|
1486
|
+
# Read in the width and height details.
|
1487
|
+
read_source(file, read, 7)
|
1488
|
+
dimensions = read[-4,4].pack('C4').unpack('nn').reverse
|
1489
|
+
done = true
|
1490
|
+
else
|
1491
|
+
# Skip the marker block.
|
1492
|
+
read_source(file, read, 2)
|
1493
|
+
read_source(file, read, read[-2,2].pack('C2').unpack('n')[0] - 2)
|
1494
|
+
end
|
1495
|
+
end
|
1496
|
+
elsif @type == PNG
|
1497
|
+
# Read in the data to contain the width and height.
|
1498
|
+
read_source(file, read, 16)
|
1499
|
+
dimensions = read[-8,8].pack('C8').unpack('N2')
|
1500
|
+
elsif @type == BITMAP
|
1501
|
+
# Read in the data to contain the width and height.
|
1502
|
+
read_source(file, read, 18)
|
1503
|
+
dimensions = [to_integer(read[-8,4]), to_integer(read[-4,4])]
|
1504
|
+
end
|
1477
1505
|
end
|
1478
1506
|
|
1479
1507
|
dimensions
|
1480
1508
|
end
|
1481
1509
|
|
1482
|
-
private :
|
1483
|
-
:get_dimensions
|
1510
|
+
private :get_file_type, :to_integer, :get_endian, :get_dimensions, :open_file
|
1484
1511
|
end # End of the ImageNode class.
|
1485
1512
|
|
1486
1513
|
|