clbustos-rtf 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|