minimalist_ods 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []