odf 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +76 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/odf.rb +65 -0
- data/lib/odf/component/abstract.rb +23 -0
- data/lib/odf/component/content.rb +35 -0
- data/lib/odf/component/image_set.rb +42 -0
- data/lib/odf/component/manifest.rb +33 -0
- data/lib/odf/component/meta.rb +25 -0
- data/lib/odf/component/mime_type.rb +11 -0
- data/lib/odf/component/settings.rb +15 -0
- data/lib/odf/component/styles.rb +26 -0
- data/lib/odf/component/xml_component.rb +29 -0
- data/lib/odf/document/abstract.rb +67 -0
- data/lib/odf/document/presentation.rb +13 -0
- data/lib/odf/document/spreadsheet.rb +13 -0
- data/lib/odf/document/text.rb +17 -0
- data/lib/odf/element/abstract.rb +34 -0
- data/lib/odf/element/abstract_contenteable.rb +11 -0
- data/lib/odf/element/binary_data.rb +13 -0
- data/lib/odf/element/draw_frame.rb +50 -0
- data/lib/odf/element/draw_page.rb +15 -0
- data/lib/odf/element/draw_text_box.rb +38 -0
- data/lib/odf/element/heading.rb +18 -0
- data/lib/odf/element/image.rb +41 -0
- data/lib/odf/element/number.rb +14 -0
- data/lib/odf/element/paragraph.rb +14 -0
- data/lib/odf/element/table.rb +19 -0
- data/lib/odf/element/table_cell.rb +30 -0
- data/lib/odf/element/table_row.rb +16 -0
- data/lib/odf/element/text_list.rb +21 -0
- data/lib/odf/element/text_list_item.rb +29 -0
- data/lib/odf/utils/file.rb +45 -0
- data/lib/odf/utils/xml.rb +13 -0
- data/lib/odf/version.rb +3 -0
- data/odf.gemspec +32 -0
- data/templates/default_style.xml +2 -0
- metadata +185 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
module Odf::Component
|
2
|
+
module XmlComponent
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval { attr_reader :xml }
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(document)
|
9
|
+
super
|
10
|
+
initialize_xml
|
11
|
+
end
|
12
|
+
|
13
|
+
def content
|
14
|
+
wrap_xml
|
15
|
+
xml.to_xml
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def initialize_xml
|
20
|
+
@xml = Odf::Utils::Xml.new
|
21
|
+
xml.encoding = "UTF-8"
|
22
|
+
end
|
23
|
+
|
24
|
+
def wrap_xml
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Odf::Document
|
2
|
+
class Abstract
|
3
|
+
|
4
|
+
COMPONENTS = [:content, :manifest, :meta, :mime_type, :styles, :settings]
|
5
|
+
|
6
|
+
attr_reader *(COMPONENTS + [:image_set])
|
7
|
+
attr_writer :creator
|
8
|
+
|
9
|
+
alias :document :itself
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@content = Odf::Component::Content.new(self)
|
13
|
+
@image_set = Odf::Component::ImageSet.new(self)
|
14
|
+
@manifest = Odf::Component::Manifest.new(self)
|
15
|
+
@meta = Odf::Component::Meta.new(self)
|
16
|
+
@mime_type = Odf::Component::MimeType.new(self)
|
17
|
+
@settings = Odf::Component::Settings.new(self)
|
18
|
+
@styles = Odf::Component::Styles.new(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the mime type of document.
|
22
|
+
# All subclasses of this abstract class
|
23
|
+
# should define the +MIME_TYPE+ constant.
|
24
|
+
def media_type
|
25
|
+
self.class::MIME_TYPE
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the file extension for
|
29
|
+
# open document format.
|
30
|
+
def extension
|
31
|
+
self.class::EXTENSION
|
32
|
+
end
|
33
|
+
|
34
|
+
def wrapper
|
35
|
+
self.class::WRAPPER
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the components of the document.
|
39
|
+
def components
|
40
|
+
COMPONENTS
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the creator information.
|
44
|
+
def creator
|
45
|
+
@creator || Odf.creator
|
46
|
+
end
|
47
|
+
|
48
|
+
# Serialize the document to given path.
|
49
|
+
def serialize(file_name)
|
50
|
+
Odf::Utils::File.serialize(file_name, self) && true
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the output stream.
|
54
|
+
def to_blob
|
55
|
+
Odf::Utils::File.output_stream(self)
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_image(uri)
|
59
|
+
image_set << uri
|
60
|
+
end
|
61
|
+
|
62
|
+
def root
|
63
|
+
content.root
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Odf::Document
|
2
|
+
class Presentation < Abstract
|
3
|
+
|
4
|
+
MIME_TYPE = 'application/vnd.oasis.opendocument.presentation'.freeze
|
5
|
+
EXTENSION = 'odp'.freeze
|
6
|
+
WRAPPER = 'office:presentation'.freeze
|
7
|
+
|
8
|
+
def add_page(options = {})
|
9
|
+
Odf::Element::DrawPage.build(self, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Odf::Document
|
2
|
+
class Spreadsheet < Abstract
|
3
|
+
|
4
|
+
MIME_TYPE = 'application/vnd.oasis.opendocument.spreadsheet'.freeze
|
5
|
+
EXTENSION = 'ods'.freeze
|
6
|
+
WRAPPER = 'office:spreadsheet'.freeze
|
7
|
+
|
8
|
+
def add_table(options = {})
|
9
|
+
Odf::Element::Table.build(self, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Odf::Document
|
2
|
+
class Text < Abstract
|
3
|
+
|
4
|
+
MIME_TYPE = 'application/vnd.oasis.opendocument.text'.freeze
|
5
|
+
EXTENSION = 'odt'.freeze
|
6
|
+
WRAPPER = 'office:text'.freeze
|
7
|
+
|
8
|
+
def add_heading(text, options = {})
|
9
|
+
Odf::Element::Heading.build(self, text, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_paragraph(text, options = {})
|
13
|
+
Odf::Element::Paragraph.build(self, text, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Odf::Element
|
2
|
+
class Abstract
|
3
|
+
|
4
|
+
attr_reader :parent, :root
|
5
|
+
|
6
|
+
def initialize(parent, options = {})
|
7
|
+
@parent = parent
|
8
|
+
@root = document.content.xml.create_element(xml_tag, fetch_options(options))
|
9
|
+
@parent.root.add_child(@root)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Accessor for valid options of element
|
13
|
+
def valid_options
|
14
|
+
@valid_options ||= defined?(self.class::VALID_OPTIONS) ? self.class::VALID_OPTIONS : {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def xml_tag
|
18
|
+
self.class::XML_TAG
|
19
|
+
end
|
20
|
+
|
21
|
+
def document
|
22
|
+
parent.document
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def fetch_options(options)
|
27
|
+
options.reduce({}) do |ret, (k, v)|
|
28
|
+
option = valid_options.fetch(k, k)
|
29
|
+
ret.merge(option => v)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Odf::Element
|
2
|
+
module AbstractContenteable
|
3
|
+
|
4
|
+
def initialize(parent, content, options = {})
|
5
|
+
@parent = parent
|
6
|
+
@root = document.content.xml.create_element(xml_tag, content, fetch_options(options))
|
7
|
+
@parent.root.add_child(@root)
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Odf::Element
|
2
|
+
class DrawFrame < Abstract
|
3
|
+
|
4
|
+
VALID_OPTIONS = {
|
5
|
+
caption_id: 'draw:caption-id',
|
6
|
+
class_names: 'draw:class-names',
|
7
|
+
copy_of: 'draw:copy-of',
|
8
|
+
id: 'draw:id',
|
9
|
+
name: 'draw:name',
|
10
|
+
style_name: 'draw:style-name',
|
11
|
+
text_style_name: 'draw:text-style-name',
|
12
|
+
transform: 'draw:transform',
|
13
|
+
z_index: 'draw:z-index',
|
14
|
+
p_class: 'presentation:class',
|
15
|
+
p_class_names: 'presentation:class-names',
|
16
|
+
placeholder: 'presentation:placeholder',
|
17
|
+
p_style_name: 'presentation:style-name',
|
18
|
+
user_transformed: 'presentation:user-transformed',
|
19
|
+
rel_height: 'style:rel-height',
|
20
|
+
rel_width: 'style:rel-width',
|
21
|
+
height: 'svg:height',
|
22
|
+
width: 'svg:width',
|
23
|
+
x: 'svg:x',
|
24
|
+
y: 'svg:y',
|
25
|
+
end_cell_address: 'table:end-cell-address',
|
26
|
+
end_x: 'table:end-x',
|
27
|
+
end_y: 'table:end-y',
|
28
|
+
table_background: 'table:table-background',
|
29
|
+
anchor_page_number: 'table:anchor-page-number',
|
30
|
+
anchor_type: 'table:anchor-type',
|
31
|
+
xml_id: 'xml:id',
|
32
|
+
layer: 'draw:layer'
|
33
|
+
}
|
34
|
+
|
35
|
+
XML_TAG = 'draw:frame'.freeze
|
36
|
+
|
37
|
+
def self.build(parent, options = {})
|
38
|
+
new(parent, options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_text_box(options = {})
|
42
|
+
Odf::Element::DrawTextBox.build(self, options)
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_image(options = {})
|
46
|
+
Odf::Element::Image.build(self, options)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Odf::Element
|
2
|
+
class DrawPage < Abstract
|
3
|
+
|
4
|
+
XML_TAG = 'draw:page'.freeze
|
5
|
+
|
6
|
+
def self.build(document, options = {})
|
7
|
+
new(document, options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_frame(options = {})
|
11
|
+
Odf::Element::DrawFrame.build(self, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Odf::Element
|
2
|
+
class DrawTextBox < Abstract
|
3
|
+
|
4
|
+
VALID_OPTIONS = {
|
5
|
+
next_name: 'draw:chain-next-name',
|
6
|
+
corner_radius: 'draw:corner-radius',
|
7
|
+
max_height: 'fo:max-height',
|
8
|
+
max_width: 'fo:max-width',
|
9
|
+
min_height: 'fo:min-height',
|
10
|
+
min_width: 'fo:min-width',
|
11
|
+
text_id: 'text:id',
|
12
|
+
xml_id: 'xml:id'
|
13
|
+
}
|
14
|
+
|
15
|
+
XML_TAG = 'draw:text-box'.freeze
|
16
|
+
|
17
|
+
def self.build(parent, options = {})
|
18
|
+
new(parent, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_frame(options = {})
|
22
|
+
Odf::Element::DrawFrame.build(self, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_heading(text, options = {})
|
26
|
+
Odf::Element::Heading.build(self, text, options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_paragraph(text, options = {})
|
30
|
+
Odf::Element::Paragraph.build(self, text, options)
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_text_list(options = {})
|
34
|
+
Odf::Element::TextList.build(self, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Odf::Element
|
2
|
+
class Heading < Abstract
|
3
|
+
|
4
|
+
include AbstractContenteable
|
5
|
+
|
6
|
+
XML_TAG = 'text:h'.freeze
|
7
|
+
|
8
|
+
def self.build(parent, text, options = {})
|
9
|
+
new(parent, text, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(parent, content, options = {})
|
13
|
+
options = { level: 1 }.merge(options)
|
14
|
+
super(parent, content, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Odf::Element
|
2
|
+
class Image < Abstract
|
3
|
+
|
4
|
+
VALID_OPTIONS = {
|
5
|
+
filter_name: 'draw:filter-name',
|
6
|
+
actuate: 'xlink:actuate',
|
7
|
+
href: 'xlink:href',
|
8
|
+
show: 'xlink:show',
|
9
|
+
type: 'xlink:type',
|
10
|
+
xml_id: 'xml:id'
|
11
|
+
}
|
12
|
+
|
13
|
+
XML_TAG = 'draw:image'.freeze
|
14
|
+
|
15
|
+
def self.build(parent, options = {})
|
16
|
+
new(parent, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(parent, options)
|
20
|
+
@parent = parent
|
21
|
+
|
22
|
+
# Save image under Pictures directory
|
23
|
+
if options.fetch(:save, true) && options[:href]
|
24
|
+
uri = save_image(options[:href])
|
25
|
+
options.merge!({ href: uri, show: 'embed' })
|
26
|
+
end
|
27
|
+
|
28
|
+
super(parent, options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_paragraph(text, options = {})
|
32
|
+
Odf::Element::Paragraph.build(self, text, options)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def save_image(href)
|
37
|
+
document.add_image(href)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Odf::Element
|
2
|
+
class Table < Abstract
|
3
|
+
|
4
|
+
VALID_OPTIONS = {
|
5
|
+
table_name: 'table:name'
|
6
|
+
}
|
7
|
+
|
8
|
+
XML_TAG = 'table:table'.freeze
|
9
|
+
|
10
|
+
def self.build(parent, options = {})
|
11
|
+
new(parent, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_table_row(options = {})
|
15
|
+
Odf::Element::TableRow.build(self, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Odf::Element
|
2
|
+
class TableCell < Abstract
|
3
|
+
|
4
|
+
TYPE_MAP = {
|
5
|
+
Fixnum => 'float',
|
6
|
+
Float => 'float',
|
7
|
+
String => 'string'
|
8
|
+
}
|
9
|
+
|
10
|
+
VALID_OPTIONS = {}
|
11
|
+
XML_TAG = 'table:table-cell'.freeze
|
12
|
+
|
13
|
+
def self.build(parent, content, options = {})
|
14
|
+
type = options[:type] || infer_type(content)
|
15
|
+
self_opts = {
|
16
|
+
'office:value-type' => type,
|
17
|
+
'office:value' => content
|
18
|
+
}.merge(options)
|
19
|
+
this = new(parent, self_opts)
|
20
|
+
|
21
|
+
Odf::Element::Paragraph.build(this, content, options)
|
22
|
+
this
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.infer_type(content)
|
26
|
+
TYPE_MAP[content.class] || 'string'
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|