rodf 1.1.1 → 1.2.0

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