rodf 0.3.7 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +77 -0
  3. data/LICENSE +7 -0
  4. data/README.md +244 -64
  5. data/Rakefile +9 -17
  6. data/lib/rodf/cell.rb +171 -0
  7. data/lib/rodf/column.rb +21 -0
  8. data/lib/rodf/container.rb +18 -0
  9. data/lib/rodf/data_style.rb +46 -0
  10. data/lib/rodf/document.rb +133 -0
  11. data/lib/rodf/hyperlink.rb +33 -0
  12. data/lib/rodf/master_page.rb +11 -0
  13. data/lib/rodf/page_layout.rb +41 -0
  14. data/lib/rodf/paragraph.rb +28 -0
  15. data/lib/rodf/paragraph_container.rb +30 -0
  16. data/lib/rodf/property.rb +138 -0
  17. data/lib/rodf/row.rb +56 -0
  18. data/lib/{odf → rodf}/skeleton/manifest.xml.erb +0 -0
  19. data/lib/{odf → rodf}/skeleton/styles.pxml +0 -0
  20. data/lib/rodf/skeleton.rb +17 -0
  21. data/lib/rodf/span.rb +45 -0
  22. data/lib/rodf/spreadsheet.rb +113 -0
  23. data/lib/rodf/style.rb +77 -0
  24. data/lib/rodf/style_section.rb +33 -0
  25. data/lib/rodf/tab.rb +17 -0
  26. data/lib/rodf/table.rb +95 -0
  27. data/lib/rodf/text.rb +126 -0
  28. data/lib/rodf/version.rb +3 -0
  29. data/lib/rodf.rb +31 -0
  30. metadata +76 -115
  31. data/CHANGELOG +0 -17
  32. data/Gemfile +0 -9
  33. data/LICENSE.LGPL +0 -166
  34. data/Manifest +0 -48
  35. data/lib/odf/cell.rb +0 -104
  36. data/lib/odf/column.rb +0 -33
  37. data/lib/odf/compatibility.rb +0 -25
  38. data/lib/odf/container.rb +0 -57
  39. data/lib/odf/data_style.rb +0 -45
  40. data/lib/odf/document.rb +0 -67
  41. data/lib/odf/hyperlink.rb +0 -51
  42. data/lib/odf/master_page.rb +0 -33
  43. data/lib/odf/page_layout.rb +0 -39
  44. data/lib/odf/paragraph.rb +0 -46
  45. data/lib/odf/paragraph_container.rb +0 -35
  46. data/lib/odf/property.rb +0 -131
  47. data/lib/odf/row.rb +0 -44
  48. data/lib/odf/skeleton.rb +0 -34
  49. data/lib/odf/span.rb +0 -70
  50. data/lib/odf/spreadsheet.rb +0 -64
  51. data/lib/odf/style.rb +0 -65
  52. data/lib/odf/style_section.rb +0 -42
  53. data/lib/odf/tab.rb +0 -38
  54. data/lib/odf/table.rb +0 -50
  55. data/lib/odf/text.rb +0 -66
  56. data/rodf.gemspec +0 -48
  57. data/spec/cell_spec.rb +0 -189
  58. data/spec/data_style_spec.rb +0 -61
  59. data/spec/file_storage_spec.rb +0 -47
  60. data/spec/hyperlink_spec.rb +0 -62
  61. data/spec/master_page_spec.rb +0 -35
  62. data/spec/page_layout_spec.rb +0 -45
  63. data/spec/paragraph_spec.rb +0 -75
  64. data/spec/property_spec.rb +0 -270
  65. data/spec/row_spec.rb +0 -59
  66. data/spec/skeleton_spec.rb +0 -33
  67. data/spec/span_spec.rb +0 -65
  68. data/spec/spec_helper.rb +0 -23
  69. data/spec/spreadsheet_spec.rb +0 -123
  70. data/spec/style_section_spec.rb +0 -42
  71. data/spec/style_spec.rb +0 -109
  72. data/spec/tab_spec.rb +0 -34
  73. data/spec/table_spec.rb +0 -90
  74. data/spec/text_spec.rb +0 -79
@@ -0,0 +1,18 @@
1
+ module RODF
2
+ class Container
3
+ def initialize(*_args, &contents)
4
+ return unless contents
5
+
6
+ if contents.arity.zero?
7
+ instance_exec(self, &contents)
8
+ else
9
+ yield(self)
10
+ end
11
+ end
12
+
13
+ def self.create(*args, &contents)
14
+ container = self.new(*args, &contents)
15
+ container.xml
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,46 @@
1
+ module RODF
2
+ class DataStyle < Container
3
+ def initialize(name, type)
4
+ super
5
+
6
+ @type, @name = type, name
7
+ end
8
+
9
+ def style_sections
10
+ @style_sections ||= []
11
+ end
12
+
13
+ def style_section(*args, &block)
14
+ x = StyleSection.new(*args, &block)
15
+
16
+ style_sections << x
17
+
18
+ return x
19
+ end
20
+ alias section style_section
21
+
22
+ def style_sections_xml
23
+ style_sections.map(&:xml).join
24
+ end
25
+
26
+ def add_style_sections(*elements)
27
+ if elements.first.is_a?(Array)
28
+ elements = elements.first
29
+ end
30
+
31
+ elements.each do |element|
32
+ style_section(element)
33
+ end
34
+ end
35
+
36
+ def xml
37
+ Builder::XmlMarkup.new.tag! "number:#{@type}-style", 'style:name' => @name do |xml|
38
+ xml << style_sections_xml
39
+ end
40
+ end
41
+
42
+ def method_missing(name, *args)
43
+ section(name, *args)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,133 @@
1
+ module RODF
2
+ class Document < Container
3
+ def self.file(ods_file_name, &contents)
4
+ doc = new
5
+
6
+ if contents.arity.zero?
7
+ contents.call(doc)
8
+ else
9
+ doc.instance_exec(doc, &contents)
10
+ end
11
+
12
+ doc.write_to ods_file_name
13
+ end
14
+
15
+ def write_to(ods_file_name)
16
+ File.open(ods_file_name, 'wb') { |f| f << self.bytes }
17
+ end
18
+
19
+ def bytes
20
+ buffer = Zip::OutputStream::write_buffer do |zio|
21
+ zio.put_next_entry('META-INF/manifest.xml')
22
+
23
+ zio << self.class.skeleton.manifest(self.class.doc_type)
24
+
25
+ zio.put_next_entry('styles.xml')
26
+
27
+ zio << self.class.skeleton.styles
28
+
29
+ zio << self.office_styles_xml unless self.office_styles.empty?
30
+
31
+ zio << "</office:styles> </office:document-styles>"
32
+
33
+ zio.put_next_entry('content.xml')
34
+
35
+ zio << self.xml
36
+ end
37
+
38
+ buffer.set_encoding('ASCII-8BIT')
39
+
40
+ buffer.rewind
41
+
42
+ buffer.sysread
43
+ end
44
+
45
+ def styles
46
+ @styles ||= []
47
+ end
48
+
49
+ def style(*args, &block)
50
+ x = Style.new(*args, &block)
51
+
52
+ styles << x
53
+
54
+ return x
55
+ end
56
+
57
+ def styles_xml
58
+ styles.map(&:xml).join
59
+ end
60
+
61
+ def add_styles(*elements)
62
+ if elements.first.is_a?(Array)
63
+ elements = elements.first
64
+ end
65
+
66
+ elements.each do |element|
67
+ style(element)
68
+ end
69
+ end
70
+
71
+ def default_styles
72
+ @default_styles ||= []
73
+ end
74
+
75
+ def default_style(*args, &block)
76
+ x = DefaultStyle.new(*args, &block)
77
+
78
+ default_styles << x
79
+
80
+ return x
81
+ end
82
+
83
+ def default_styles_xml
84
+ default_styles.map(&:xml).join
85
+ end
86
+
87
+ def add_default_styles(*elements)
88
+ if elements.first.is_a?(Array)
89
+ elements = elements.first
90
+ end
91
+
92
+ elements.each do |element|
93
+ default_style(element)
94
+ end
95
+ end
96
+
97
+ def office_styles
98
+ @office_styles ||= []
99
+ end
100
+
101
+ def office_style(*args, &block)
102
+ x = OfficeStyle.new(*args, &block)
103
+
104
+ office_styles << x
105
+
106
+ return x
107
+ end
108
+
109
+ def office_styles_xml
110
+ office_styles.map(&:xml).join
111
+ end
112
+
113
+ def add_office_styles(*elements)
114
+ if elements.first.is_a?(Array)
115
+ elements = elements.first
116
+ end
117
+
118
+ elements.each do |element|
119
+ office_style(element)
120
+ end
121
+ end
122
+
123
+ private
124
+
125
+ def self.skeleton
126
+ @skeleton ||= Skeleton.new
127
+ end
128
+
129
+ def self.doc_type
130
+ name.split('::').last.downcase
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,33 @@
1
+ module RODF
2
+ class Hyperlink < ParagraphContainer
3
+ def initialize(first, second = {})
4
+ super
5
+
6
+ if second.instance_of?(Hash) && second.empty?
7
+ @href = first
8
+ else
9
+ span(first)
10
+ @href = second.instance_of?(Hash) ? second[:href] : second
11
+ end
12
+ end
13
+
14
+ def xml
15
+ Builder::XmlMarkup.new.text:a, 'xlink:href' => @href do |a|
16
+ a << content_parts_xml
17
+ end
18
+ end
19
+ end
20
+
21
+ class ParagraphContainer < Container
22
+ def link(*args)
23
+ l = Hyperlink.new(*args)
24
+
25
+ yield l if block_given?
26
+
27
+ content_parts << l
28
+
29
+ l
30
+ end
31
+ alias a link
32
+ end
33
+ end
@@ -0,0 +1,11 @@
1
+ module RODF
2
+ class MasterPage
3
+ def initialize(name, opts = {})
4
+ @name, @layout = name, opts[:layout]
5
+ end
6
+
7
+ def xml
8
+ Builder::XmlMarkup.new.tag! 'style:master-page', 'style:name' => @name, 'style:page-layout-name' => @layout
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,41 @@
1
+ module RODF
2
+ class PageLayout < Container
3
+ def initialize(name)
4
+ super
5
+
6
+ @name = name
7
+ end
8
+
9
+ def properties
10
+ @properties ||= []
11
+ end
12
+
13
+ def property(*args, &block)
14
+ x = Property.new(*args, &block)
15
+
16
+ properties << x
17
+
18
+ return x
19
+ end
20
+
21
+ def properties_xml
22
+ properties.map(&:xml).join
23
+ end
24
+
25
+ def add_properties(*elements)
26
+ if elements.first.is_a?(Array)
27
+ elements = elements.first
28
+ end
29
+
30
+ elements.each do |element|
31
+ property(element)
32
+ end
33
+ end
34
+
35
+ def xml
36
+ Builder::XmlMarkup.new.tag! 'style:page-layout', 'style:name' => @name do |b|
37
+ b << properties_xml
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,28 @@
1
+ module RODF
2
+ class Paragraph < ParagraphContainer
3
+ def initialize(fst = nil, snd = {})
4
+ super
5
+
6
+ first_is_hash = fst.instance_of?(Hash)
7
+
8
+ span(fst) unless first_is_hash
9
+
10
+ @elem_attrs = make_element_attributes(first_is_hash ? fst : snd)
11
+ end
12
+
13
+ def xml
14
+ Builder::XmlMarkup.new.text:p, @elem_attrs do |xml|
15
+ xml << content_parts_xml
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def make_element_attributes(opts)
22
+ attrs = {}
23
+ attrs['text:style-name'] = opts[:style] unless opts[:style].nil?
24
+ attrs
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ module RODF
2
+ # Container for all kinds of paragraph content
3
+ class ParagraphContainer < Container
4
+ def content_parts
5
+ @content_parts ||= []
6
+ end
7
+
8
+ def content_parts_xml
9
+ content_parts.map {|p| p.xml}.join
10
+ end
11
+
12
+ def span(*args)
13
+ s = Span.new(*args)
14
+
15
+ yield s if block_given?
16
+
17
+ content_parts << s
18
+
19
+ s
20
+ end
21
+
22
+ def <<(content)
23
+ span(content)
24
+ end
25
+
26
+ def method_missing(style, *args)
27
+ span(style, *args)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,138 @@
1
+ module RODF
2
+ class Property
3
+ PROPERTY_NAMES = {
4
+ cell: 'table-cell-properties',
5
+ text: 'text-properties',
6
+ column: 'table-column-properties',
7
+ row: 'table-row-properties',
8
+ page_layout: 'page-layout-properties',
9
+ header_footer: 'header-footer-properties',
10
+ list_level: 'list-level-properties',
11
+ conditional: 'map',
12
+ }
13
+
14
+ TRANSLATED_SPECS = [:border_color, :border_style, :border_width]
15
+
16
+ ATTRIBUTES_TO_NAMESPACES = [
17
+ [
18
+ [
19
+ 'column-width', 'rotation-angle', 'text-underline-type', 'tab-stop-distance',
20
+ 'condition', 'apply-style-name', 'base-cell-address', 'row-height',
21
+ 'country-asian', 'country-complex', 'font-charset', 'font-charset-asian',
22
+ 'font-charset-complex', 'font-family-asian', 'font-family-complex', 'font-family-generic',
23
+ 'font-family-generic-asian', 'font-family-generic-complex', 'font-name', 'font-name-asian',
24
+ 'font-name-complex', 'font-pitch', 'font-pitch-asian', 'font-pitch-complex', 'font-relief',
25
+ 'font-size-asian', 'font-size-complex', 'font-size-rel', 'font-size-rel-asian',
26
+ 'font-size-rel-complex', 'font-style-asian', 'font-style-complex', 'font-style-name',
27
+ 'font-style-name-asian', 'font-style-name-complex', 'font-weight-asian', 'font-weight-complex',
28
+ 'language-asian', 'language-complex', 'letter-kerning', 'rfc-language-tag',
29
+ 'rfc-language-tag-asian', 'rfc-language-tag-complex', 'script-asian', 'script-complex',
30
+ 'script-type', 'text-blinking', 'text-combine', 'text-combine-end-char',
31
+ 'text-combine-start-char', 'text-emphasize', 'text-line-through-color',
32
+ 'text-line-through-mode', 'text-line-through-style', 'text-line-through-text',
33
+ 'text-line-through-text-style', 'text-line-through-type', 'text-line-through-width',
34
+ 'text-outline', 'text-overline-color', 'text-overline-mode', 'text-overline-style',
35
+ 'text-overline-type', 'text-overline-width', 'text-position', 'text-rotation-angle',
36
+ 'text-rotation-scale', 'text-scale', 'text-underline-color', 'text-underline-mode',
37
+ 'text-underline-style', 'text-underline-width', 'use-window-font-color',
38
+ 'border-line-width', 'border-line-width-bottom', 'border-line-width-left',
39
+ 'border-line-width-right', 'border-line-width-top', 'cell-protect', 'decimal-places',
40
+ 'diagonal-bl-tr', 'diagonal-bl-tr-widths', 'diagonal-tl-br', 'diagonal-tl-br-widths',
41
+ 'direction', 'glyph-orientation-vertical', 'print-content', 'repeat-content',
42
+ 'rotation-align', 'shadow', 'shrink-to-fit', 'text-align-source',
43
+ 'vertical-align', 'writing-mode', 'min-row-height', 'use-optimal-row-height',
44
+ 'rel-column-width', 'use-optimal-column-width', 'auto-text-indent', 'background-transparency',
45
+ 'font-independent-line-spacing', 'join-border', 'justify-single-word', 'line-break',
46
+ 'line-height-at-least', 'line-spacing', 'page-number', 'punctuation-wrap', 'register-true',
47
+ 'snap-to-layout-grid', 'text-autospace',
48
+ 'writing-mode-automatic',
49
+ 'first-page-number', 'footnote-max-height', 'layout-grid-base-height', 'layout-grid-base-width',
50
+ 'layout-grid-color', 'layout-grid-display', 'layout-grid-lines', 'layout-grid-mode',
51
+ 'layout-grid-print', 'layout-grid-ruby-below', 'layout-grid-ruby-height', 'layout-grid-snap-to',
52
+ 'layout-grid-standard-mode', 'num-format', 'num-letter-sync', 'num-prefix', 'num-suffix',
53
+ 'paper-tray-name', 'print', 'print-orientation', 'print-page-order',
54
+ 'register-truth-ref-style-name', 'scale-to', 'scale-to-pages', 'table-centering',
55
+ 'dynamic-spacing',
56
+ 'ruby-align', 'ruby-position',
57
+ 'editable', 'protect',
58
+ 'may-break-between-rows', 'rel-width', 'width',
59
+ 'vertical-pos', 'vertical-rel'
60
+ ],
61
+ 'style'
62
+ ],
63
+ [
64
+ ['height', 'y'],
65
+ 'svg'
66
+ ],
67
+ [
68
+ ['dont-balance-text-columns', 'list-level-position-and-space-mode', 'min-label-distance', 'min-label-width', 'space-before'],
69
+ 'text'
70
+ ],
71
+ [
72
+ ['align', 'border-model', 'display'],
73
+ 'table'
74
+ ]
75
+ ]
76
+
77
+ def initialize(type, specs={})
78
+ @name = 'style:' + (PROPERTY_NAMES[type] || "#{type}-properties")
79
+
80
+ @specs = translate(specs).map { |k, v| [k.to_s, v] }
81
+ end
82
+
83
+ def xml
84
+ specs = @specs.inject({}) do |acc, kv|
85
+ acc.merge Property.lookup_namespace_for(kv.first) + ':' + kv.first => kv.last
86
+ end
87
+
88
+ Builder::XmlMarkup.new.tag! @name, specs
89
+ end
90
+
91
+ def self.lookup_namespace_for(property_name)
92
+ as = ATTRIBUTES_TO_NAMESPACES.select {|a| a[0].include? property_name}
93
+
94
+ as.empty? ? 'fo' : as[0][1]
95
+ end
96
+
97
+ private
98
+
99
+ def translate(specs)
100
+ result = specs.clone
101
+
102
+ tspecs = specs.select {|k, v| TRANSLATED_SPECS.include? k}
103
+
104
+ tspecs.map {|k, v| result.delete k}
105
+
106
+ tspecs = tspecs.inject({}) {|acc, e| acc.merge e.first => e.last}
107
+
108
+ if tspecs[:border_width] && tspecs[:border_style] && tspecs[:border_color] then
109
+ width = tspecs[:border_width].split
110
+ style = tspecs[:border_style].split
111
+ color = tspecs[:border_color].split
112
+
113
+ if width.length == 1 && style.length == 1 && color.length == 1 then
114
+ result[:border] = [width[0], style[0], color[0]].join(' ')
115
+ else
116
+ result['border-top'] = cascading_join(width, style, color, 0)
117
+ result['border-right'] = cascading_join(width, style, color, 1, 0)
118
+ result['border-bottom'] = cascading_join(width, style, color, 2, 0)
119
+ result['border-left'] = cascading_join(width, style, color, 3, 1, 0)
120
+ end
121
+ end
122
+
123
+ result
124
+ end
125
+
126
+ def cascading_join(width_parts, style_parts, color_parts, *prefs)
127
+ [
128
+ cascade(width_parts, prefs),
129
+ cascade(style_parts, prefs),
130
+ cascade(color_parts, prefs),
131
+ ].join(' ')
132
+ end
133
+
134
+ def cascade(list, prefs)
135
+ prefs.inject(nil) {|acc, i| acc || list[i]}
136
+ end
137
+ end
138
+ end
data/lib/rodf/row.rb ADDED
@@ -0,0 +1,56 @@
1
+ module RODF
2
+ class Row < Container
3
+ attr_reader :number
4
+ attr_writer :style
5
+
6
+ def initialize(number=0, opts={})
7
+ @number = number
8
+
9
+ @elem_attrs = {}
10
+
11
+ @elem_attrs['table:style-name'] = opts[:style] unless opts[:style].nil?
12
+
13
+ if opts[:attributes]
14
+ @elem_attrs.merge!(opts[:attributes])
15
+ end
16
+
17
+ super
18
+ end
19
+
20
+ def cells
21
+ @cells ||= []
22
+ end
23
+
24
+ def cell(*args, &block)
25
+ x = Cell.new(*args, &block)
26
+
27
+ cells << x
28
+
29
+ return x
30
+ end
31
+
32
+ def cells_xml
33
+ cells.map(&:xml).join
34
+ end
35
+
36
+ def style=(style_name)
37
+ @elem_attrs['table:style-name'] = style_name
38
+ end
39
+
40
+ def add_cells(*elements)
41
+ if elements.first.is_a?(Array)
42
+ elements = elements.first
43
+ end
44
+
45
+ elements.each do |element|
46
+ cell(element)
47
+ end
48
+ end
49
+
50
+ def xml
51
+ Builder::XmlMarkup.new.tag! 'table:table-row', @elem_attrs do |xml|
52
+ xml << cells_xml
53
+ end
54
+ end
55
+ end
56
+ end
File without changes
File without changes
@@ -0,0 +1,17 @@
1
+ module RODF
2
+ class Skeleton
3
+ def manifest(document_type)
4
+ ERB.new(template('manifest.xml.erb')).result(binding)
5
+ end
6
+
7
+ def styles
8
+ template('styles.pxml')
9
+ end
10
+
11
+ private
12
+
13
+ def template(fname)
14
+ File.open(File.dirname(__FILE__) + '/skeleton/' + fname).read
15
+ end
16
+ end
17
+ end
data/lib/rodf/span.rb ADDED
@@ -0,0 +1,45 @@
1
+ module RODF
2
+ class TextNode
3
+ def initialize(content)
4
+ @content = content
5
+ end
6
+
7
+ def xml
8
+ Builder::XChar.encode(@content.to_s)
9
+ end
10
+ end
11
+
12
+ class Span < ParagraphContainer
13
+ def initialize(first, second=nil, style: nil, &block)
14
+ super(first, second, &block)
15
+
16
+ @style = nil
17
+
18
+ if first.instance_of?(Symbol)
19
+ ### Legacy behaviour
20
+
21
+ @style = first
22
+
23
+ if !second.nil?
24
+ content_parts << TextNode.new(second)
25
+ end
26
+ else
27
+ if style
28
+ @style = style
29
+ end
30
+
31
+ content_parts << TextNode.new(first)
32
+ end
33
+ end
34
+
35
+ def xml
36
+ if @style.nil?
37
+ return content_parts_xml
38
+ end
39
+
40
+ Builder::XmlMarkup.new.text(:span, 'text:style-name' => @style) do |xml|
41
+ xml << content_parts_xml
42
+ end
43
+ end
44
+ end
45
+ end