minimalist_ods 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/minimalist_ods.rb +215 -0
  3. metadata +58 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 463a57f017a1b0a8761d32fc7a52d9a191a03750725489eb8e12b9e0a0d4cdca
4
+ data.tar.gz: f3afeb53140f7e7ded4e96b786ec889adc55cbbd8644e20dbd00653d763cac62
5
+ SHA512:
6
+ metadata.gz: 6954e7a37852a4c3b72b3422667755a557818dcc1aa80f07b448e60fe82d0e6e2ae42c418ff28f5b0c1f59e29275928c6ef47660734fc5955557d930c4351d56
7
+ data.tar.gz: 45be33c3a46aa0b9f2ce7bfbba3dc237a2bb14fe8ae4cc3cdd125f68a5819c61c15f9817fd97ab352174d6937035db8c4bbbbff3be1975f4fec235400b7ab66e
@@ -0,0 +1,215 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A Ruby Minimalist ODS
4
+ require 'rubygems'
5
+ require 'zip'
6
+ require 'date'
7
+
8
+ class MinimalistODS
9
+ MIMETYPE = 'application/vnd.oasis.opendocument.spreadsheet'
10
+ META_TEMPLATE = <<~XML
11
+ <?xml version="1.0" encoding="UTF-8"?>
12
+ <office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
13
+ xmlns:xlink="http://www.w3.org/1999/xlink"
14
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
15
+ xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0"
16
+ xmlns:ooo="http://openoffice.org/2004/office"
17
+ xmlns:grddl="http://www.w3.org/2003/g/data-view#"
18
+ grddl:transformation="http://docs.oasis-open.org/office/1.2/xslt/odf2rdf.xsl"
19
+ office:version="1.2">
20
+ <office:meta>
21
+ <meta:generator>ARMO</meta:generator>
22
+ <meta:initial-creator>:CREATOR</meta:initial-creator>
23
+ <dc:creator>:CREATOR</dc:creator>
24
+ <meta:creation-date>:TIME</meta:creation-date>
25
+ <dc:date>:TIME</dc:date>
26
+ <meta:editing-cycles>1</meta:editing-cycles>
27
+ </office:meta>
28
+ </office:document-meta>
29
+ XML
30
+
31
+ MANIFEST_TEMPLATE = <<~XML
32
+ <?xml version="1.0" encoding="UTF-8"?>
33
+ <manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
34
+ <manifest:file-entry manifest:media-type="application/vnd.oasis.opendocument.spreadsheet" manifest:version="1.2" manifest:full-path="/"/>
35
+ <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="content.xml"/>
36
+ <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="meta.xml"/>
37
+ </manifest:manifest>
38
+ XML
39
+
40
+ CONTENT_HEADER = <<~XML
41
+ <?xml version="1.0" encoding="UTF-8"?>
42
+ <office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2">
43
+ <office:scripts/>
44
+ <office:font-face-decls>
45
+ <style:font-face style:name="Liberation Sans" svg:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable"/>
46
+ <style:font-face style:name="Mangal" svg:font-family="Mangal" style:font-family-generic="system" style:font-pitch="variable"/>
47
+ <style:font-face style:name="Microsoft YaHei" svg:font-family="&apos;Microsoft YaHei&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
48
+ <style:font-face style:name="Segoe UI" svg:font-family="&apos;Segoe UI&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
49
+ <style:font-face style:name="Tahoma" svg:font-family="Tahoma" style:font-family-generic="system" style:font-pitch="variable"/>
50
+ </office:font-face-decls>
51
+ <office:automatic-styles>
52
+ <style:style style:name="co1" style:family="table-column">
53
+ <style:table-column-properties fo:break-before="auto" style:column-width="22.58mm"/>
54
+ </style:style>
55
+ <style:style style:name="ro1" style:family="table-row">
56
+ <style:table-row-properties style:row-height="4.52mm" fo:break-before="auto" style:use-optimal-row-height="true"/>
57
+ </style:style>
58
+ <style:style style:name="ta1" style:family="table" style:master-page-name="Default">
59
+ <style:table-properties table:display="true" style:writing-mode="lr-tb"/>
60
+ </style:style>
61
+ </office:automatic-styles>
62
+ <office:body>
63
+ <office:spreadsheet>
64
+ <table:calculation-settings table:automatic-find-labels="false"/>
65
+ XML
66
+
67
+ CONTENT_FOOTER = <<~XML
68
+ <table:named-expressions/>
69
+ </office:spreadsheet>
70
+ </office:body>
71
+ </office:document-content>
72
+ XML
73
+
74
+ TABLE_TEMPLATE = <<~XML
75
+ <table:table table:name=":NAME" table:style-name="ta1">
76
+ <table:table-column table:style-name="co1" table:number-columns-repeated=":COL_NUMBER" table:default-cell-style-name="Default"/>
77
+ XML
78
+
79
+ ROW_TEMPLATE = <<~XML
80
+ <table:table-row table:style-name="ro1">
81
+ :CELLS
82
+ </table:table-row>
83
+ XML
84
+
85
+ NUMERIC_CELL_TEMPLATE = <<~XML
86
+ <table:table-cell office:value-type="float" office:value=":VALUE">
87
+ <text:p>:VALUE</text:p>
88
+ </table:table-cell>
89
+ XML
90
+
91
+ TEXT_CELL_TEMPLATE = <<~XML
92
+ <table:table-cell office:value-type="string" calcext:value-type="string">
93
+ <text:p>:VALUE</text:p>
94
+ </table:table-cell>
95
+ XML
96
+
97
+ TABLE_OPEN = 1
98
+ TABLE_CLOSED = 0
99
+
100
+ attr_reader :zip, :save_as, :creator, :buffer
101
+
102
+ def initialize(save_as, creator = 'minimalist-ods')
103
+ @save_as = save_as
104
+ @creator = creator
105
+ init_zip!
106
+ init_mimetype!
107
+ init_meta!
108
+ init_manifest!
109
+ init_content!
110
+ end
111
+
112
+ def init_zip!
113
+ @zip = Zip::File.open(save_as, Zip::File::CREATE)
114
+ end
115
+
116
+ def init_mimetype!
117
+ write_to_zip('mimetype', MIMETYPE)
118
+ end
119
+
120
+ def init_meta!
121
+ meta = META_TEMPLATE.gsub(':CREATOR', creator).gsub(':TIME', Time.now.strftime('%Y-%m-%dT%H:%M:%S'))
122
+ write_to_zip('meta.xml', meta)
123
+ end
124
+
125
+ def init_manifest!
126
+ write_to_zip('META-INF/manifest.xml', MANIFEST_TEMPLATE)
127
+ end
128
+
129
+ def init_content!
130
+ @buffer = @zip.get_output_stream('content.xml')
131
+ @status = TABLE_CLOSED
132
+ buffer.write(CONTENT_HEADER)
133
+ end
134
+
135
+ class MinimalistOODSError < StandardError
136
+ end
137
+
138
+ class TableAlreadyOpened < MinimalistOODSError
139
+ def initialize
140
+ super('The last table is still opened')
141
+ end
142
+ end
143
+
144
+ class InvalidRowLength < MinimalistOODSError
145
+ def initialize(expected, got)
146
+ super("The number of rows doesn't match. Expected: #{@expected}, got: #{got}")
147
+ end
148
+ end
149
+
150
+ class TableNotOpened < MinimalistOODSError
151
+ def initialize
152
+ super('Currently, there is not table opened')
153
+ end
154
+ end
155
+
156
+ class InvalidParameter < MinimalistOODSError
157
+ end
158
+
159
+ def open_table(table_name, cols_number)
160
+ raise TableAlreadyOpened if @status == TABLE_OPEN
161
+ raise InvalidParameter, "Got invalid value `#{cols_number}' for table size" if cols_number.nil? || !cols_number.is_a?(Integer) || !cols_number.positive?
162
+ @cols_number = cols_number
163
+
164
+ table_header = TABLE_TEMPLATE.gsub(':NAME', table_name).gsub(':COL_NUMBER', cols_number.to_s)
165
+ buffer.write(table_header)
166
+ @status = TABLE_OPEN
167
+ end
168
+
169
+ def add_row(row)
170
+ raise InvalidRowLength.new(@cols_number, row.size) if row.size != @cols_number
171
+
172
+ cells = row.map { |cell| cell_to_xml(cell) }.join
173
+ buffer.write(ROW_TEMPLATE.gsub(':CELLS', cells))
174
+ end
175
+
176
+
177
+ def close_table
178
+ raise TableNotOpened if @status == TABLE_CLOSED
179
+
180
+ buffer.write('</table:table>')
181
+ @status = TABLE_CLOSED
182
+ end
183
+
184
+ def close_file
185
+ buffer.write(CONTENT_FOOTER)
186
+ buffer.close
187
+ zip.close
188
+ end
189
+
190
+ private
191
+
192
+ def write_to_zip(file_name, content)
193
+ stream = zip.get_output_stream(file_name)
194
+ stream.write(content)
195
+ stream.close
196
+ end
197
+
198
+ def cell_to_xml(cell)
199
+ if numeric?(cell.to_s)
200
+ NUMERIC_CELL_TEMPLATE.gsub(':VALUE', cell.to_s)
201
+ else
202
+ TEXT_CELL_TEMPLATE.gsub(':VALUE', normalize(cell.to_s))
203
+ end
204
+ end
205
+
206
+ def numeric?(str)
207
+ true if Float(str)
208
+ rescue StandardError
209
+ false
210
+ end
211
+
212
+ def normalize(str)
213
+ str.gsub(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F]/, '').gsub(/[&<>]/, '&' => '&amp;', '<' => '&lt;', '>' => '&gt;').encode('UTF-8')
214
+ end
215
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: minimalist_ods
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gilberto Vargas
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-07-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubyzip
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: A minimalist ODS generator written in Ruby
28
+ email:
29
+ - tachoguitar@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/minimalist_ods.rb
35
+ homepage: https://github.com/TachoMex/minimalist-ods
36
+ licenses:
37
+ - MIT
38
+ metadata: {}
39
+ post_install_message:
40
+ rdoc_options: []
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubygems_version: 3.5.14
55
+ signing_key:
56
+ specification_version: 4
57
+ summary: A minimalist ODS generator
58
+ test_files: []