axlsx 1.0.12 → 1.0.14
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.
- data/README.md +165 -134
- data/examples/example.rb +133 -153
- data/lib/axlsx/#cfb.xlsx# +0 -0
- data/lib/axlsx/content_type/content_type.rb +23 -0
- data/lib/axlsx/content_type/default.rb +37 -0
- data/lib/axlsx/content_type/override.rb +37 -0
- data/lib/axlsx/doc_props/app.rb +178 -0
- data/lib/axlsx/doc_props/core.rb +34 -0
- data/lib/axlsx/drawing/axis.rb +93 -0
- data/lib/axlsx/drawing/bar_3D_chart.rb +136 -0
- data/lib/axlsx/drawing/bar_series.rb +64 -0
- data/lib/axlsx/drawing/cat_axis.rb +63 -0
- data/lib/axlsx/drawing/cat_axis_data.rb +35 -0
- data/lib/axlsx/drawing/chart.rb +193 -0
- data/lib/axlsx/drawing/drawing.rb +137 -0
- data/lib/axlsx/drawing/graphic_frame.rb +52 -0
- data/lib/axlsx/drawing/line_3D_chart.rb +114 -0
- data/lib/axlsx/drawing/line_series.rb +46 -0
- data/lib/axlsx/drawing/marker.rb +61 -0
- data/lib/axlsx/drawing/one_cell_anchor.rb +89 -0
- data/lib/axlsx/drawing/pic.rb +153 -0
- data/lib/axlsx/drawing/picture_locking.rb +72 -0
- data/lib/axlsx/drawing/picture_locking.rb~ +36 -0
- data/lib/axlsx/drawing/pie_3D_chart.rb +41 -0
- data/lib/axlsx/drawing/pie_series.rb +56 -0
- data/lib/axlsx/drawing/scaling.rb +59 -0
- data/lib/axlsx/drawing/ser_axis.rb +45 -0
- data/lib/axlsx/drawing/series.rb +71 -0
- data/lib/axlsx/drawing/series_title.rb +22 -0
- data/lib/axlsx/drawing/title.rb +65 -0
- data/lib/axlsx/drawing/two_cell_anchor.rb +76 -0
- data/lib/axlsx/drawing/val_axis.rb +34 -0
- data/lib/axlsx/drawing/val_axis_data.rb +26 -0
- data/lib/axlsx/drawing/view_3D.rb +85 -0
- data/lib/axlsx/package.rb +223 -0
- data/lib/axlsx/rels/relationship.rb +44 -0
- data/lib/axlsx/rels/relationships.rb +25 -0
- data/lib/axlsx/stylesheet/border.rb +57 -0
- data/lib/axlsx/stylesheet/border_pr.rb +68 -0
- data/lib/axlsx/stylesheet/cell_alignment.rb +105 -0
- data/lib/axlsx/stylesheet/cell_protection.rb +36 -0
- data/lib/axlsx/stylesheet/cell_style.rb +65 -0
- data/lib/axlsx/stylesheet/color.rb +69 -0
- data/lib/axlsx/stylesheet/fill.rb +32 -0
- data/lib/axlsx/stylesheet/font.rb +139 -0
- data/lib/axlsx/stylesheet/gradient_fill.rb +76 -0
- data/lib/axlsx/stylesheet/gradient_stop.rb +33 -0
- data/lib/axlsx/stylesheet/num_fmt.rb +63 -0
- data/lib/axlsx/stylesheet/pattern_fill.rb +66 -0
- data/lib/axlsx/stylesheet/styles.rb +298 -0
- data/lib/axlsx/stylesheet/table_style.rb +47 -0
- data/lib/axlsx/stylesheet/table_style_element.rb +71 -0
- data/lib/axlsx/stylesheet/table_styles.rb +39 -0
- data/lib/axlsx/stylesheet/xf.rb +138 -0
- data/lib/axlsx/util/constants.rb +216 -0
- data/lib/axlsx/util/ms_off_crypto.rb +88 -0
- data/lib/axlsx/util/ms_off_crypto.rb~ +3 -0
- data/lib/axlsx/util/ms_offcrypto.rb~ +0 -0
- data/lib/axlsx/util/parser.rb +43 -0
- data/lib/axlsx/util/parser.rb~ +6 -0
- data/lib/axlsx/util/simple_typed_list.rb +160 -0
- data/lib/axlsx/util/validators.rb +132 -0
- data/lib/axlsx/version.rb +4 -0
- data/lib/axlsx/workbook/workbook.rb +161 -0
- data/lib/axlsx/workbook/worksheet/cell.rb +350 -0
- data/lib/axlsx/workbook/worksheet/row.rb +107 -0
- data/lib/axlsx/workbook/worksheet/worksheet.rb +335 -0
- data/lib/axlsx.rb +59 -0
- data/test/drawing/tc_axis.rb +1 -1
- data/test/drawing/tc_cat_axis.rb +1 -1
- data/test/drawing/tc_line_series.tc~ +34 -0
- data/test/drawing/tc_picture_locking.rb~ +77 -0
- data/test/tc_package.rb +10 -0
- data/test/workbook/worksheet/tc_cell.rb +15 -0
- data/test/workbook/worksheet/tc_worksheet.rb +22 -1
- metadata +117 -5
- data/examples/follow_20111202.xlsx +0 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
module Axlsx
|
|
2
|
+
|
|
3
|
+
# XML Encoding
|
|
4
|
+
ENCODING = "UTF-8"
|
|
5
|
+
|
|
6
|
+
# spreadsheetML namespace
|
|
7
|
+
XML_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
|
8
|
+
|
|
9
|
+
# content-types namespace
|
|
10
|
+
XML_NS_T = "http://schemas.openxmlformats.org/package/2006/content-types"
|
|
11
|
+
|
|
12
|
+
# extended-properties namespace
|
|
13
|
+
APP_NS = "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"
|
|
14
|
+
|
|
15
|
+
# doc props namespace
|
|
16
|
+
APP_NS_VT = "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"
|
|
17
|
+
|
|
18
|
+
# core properties namespace
|
|
19
|
+
CORE_NS = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties"
|
|
20
|
+
|
|
21
|
+
# dc elements (core) namespace
|
|
22
|
+
CORE_NS_DC = "http://purl.org/dc/elements/1.1/"
|
|
23
|
+
|
|
24
|
+
# dcmit (core) namespcace
|
|
25
|
+
CORE_NS_DCMIT = "http://purl.org/dc/dcmitype/"
|
|
26
|
+
|
|
27
|
+
# dc terms namespace
|
|
28
|
+
CORE_NS_DCT = "http://purl.org/dc/terms/"
|
|
29
|
+
|
|
30
|
+
# xml schema namespace
|
|
31
|
+
CORE_NS_XSI = "http://www.w3.org/2001/XMLSchema-instance"
|
|
32
|
+
|
|
33
|
+
# spreadsheet drawing namespace
|
|
34
|
+
XML_NS_XDR = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
|
|
35
|
+
|
|
36
|
+
# drawing namespace
|
|
37
|
+
XML_NS_A = "http://schemas.openxmlformats.org/drawingml/2006/main"
|
|
38
|
+
|
|
39
|
+
# chart namespace
|
|
40
|
+
XML_NS_C = "http://schemas.openxmlformats.org/drawingml/2006/chart"
|
|
41
|
+
|
|
42
|
+
# relationships namespace
|
|
43
|
+
XML_NS_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# relationships name space
|
|
47
|
+
RELS_R = "http://schemas.openxmlformats.org/package/2006/relationships"
|
|
48
|
+
|
|
49
|
+
# table rels namespace
|
|
50
|
+
TABLE_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table"
|
|
51
|
+
|
|
52
|
+
# workbook rels namespace
|
|
53
|
+
WORKBOOK_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
|
|
54
|
+
|
|
55
|
+
# worksheet rels namespace
|
|
56
|
+
WORKSHEET_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
|
|
57
|
+
|
|
58
|
+
# app rels namespace
|
|
59
|
+
APP_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"
|
|
60
|
+
|
|
61
|
+
# core rels namespace
|
|
62
|
+
CORE_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/metadata/core-properties"
|
|
63
|
+
|
|
64
|
+
# styles rels namespace
|
|
65
|
+
STYLES_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"
|
|
66
|
+
|
|
67
|
+
# drawing rels namespace
|
|
68
|
+
DRAWING_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
|
|
69
|
+
|
|
70
|
+
# chart rels namespace
|
|
71
|
+
CHART_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"
|
|
72
|
+
|
|
73
|
+
# image rels namespace
|
|
74
|
+
IMAGE_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
|
|
75
|
+
|
|
76
|
+
# table content type
|
|
77
|
+
TABLE_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"
|
|
78
|
+
|
|
79
|
+
# workbook content type
|
|
80
|
+
WORKBOOK_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"
|
|
81
|
+
|
|
82
|
+
# app content type
|
|
83
|
+
APP_CT = "application/vnd.openxmlformats-officedocument.extended-properties+xml"
|
|
84
|
+
|
|
85
|
+
# rels content type
|
|
86
|
+
RELS_CT = "application/vnd.openxmlformats-package.relationships+xml"
|
|
87
|
+
|
|
88
|
+
# styles content type
|
|
89
|
+
STYLES_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"
|
|
90
|
+
|
|
91
|
+
# xml content type
|
|
92
|
+
XML_CT = "application/xml"
|
|
93
|
+
|
|
94
|
+
# worksheet content type
|
|
95
|
+
WORKSHEET_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"
|
|
96
|
+
|
|
97
|
+
# shared strings content type
|
|
98
|
+
SHARED_STRINGS_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"
|
|
99
|
+
|
|
100
|
+
# core content type
|
|
101
|
+
CORE_CT = "application/vnd.openxmlformats-package.core-properties+xml"
|
|
102
|
+
|
|
103
|
+
# chart content type
|
|
104
|
+
CHART_CT = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
|
|
105
|
+
|
|
106
|
+
# jpeg content type
|
|
107
|
+
JPEG_CT = "image/jpeg"
|
|
108
|
+
|
|
109
|
+
# gif content type
|
|
110
|
+
GIF_CT = "image/gif"
|
|
111
|
+
|
|
112
|
+
# png content type
|
|
113
|
+
PNG_CT = "image/png"
|
|
114
|
+
|
|
115
|
+
#drawing content type
|
|
116
|
+
DRAWING_CT = "application/vnd.openxmlformats-officedocument.drawing+xml"
|
|
117
|
+
|
|
118
|
+
# xml content type extensions
|
|
119
|
+
XML_EX = "xml"
|
|
120
|
+
|
|
121
|
+
# jpeg extension
|
|
122
|
+
JPEG_EX = "jpeg"
|
|
123
|
+
|
|
124
|
+
# gif extension
|
|
125
|
+
GIF_EX = "gif"
|
|
126
|
+
|
|
127
|
+
# png extension
|
|
128
|
+
PNG_EX = "png"
|
|
129
|
+
|
|
130
|
+
# rels content type extension
|
|
131
|
+
RELS_EX = "rels"
|
|
132
|
+
|
|
133
|
+
# workbook part
|
|
134
|
+
WORKBOOK_PN = "xl/workbook.xml"
|
|
135
|
+
|
|
136
|
+
# styles part
|
|
137
|
+
STYLES_PN = "styles.xml"
|
|
138
|
+
|
|
139
|
+
# app part
|
|
140
|
+
APP_PN = "docProps/app.xml"
|
|
141
|
+
|
|
142
|
+
# core part
|
|
143
|
+
CORE_PN = "docProps/core.xml"
|
|
144
|
+
|
|
145
|
+
# content types part
|
|
146
|
+
CONTENT_TYPES_PN = "[Content_Types].xml"
|
|
147
|
+
|
|
148
|
+
# rels part
|
|
149
|
+
RELS_PN = "_rels/.rels"
|
|
150
|
+
|
|
151
|
+
# workbook rels part
|
|
152
|
+
WORKBOOK_RELS_PN = "xl/_rels/workbook.xml.rels"
|
|
153
|
+
|
|
154
|
+
# worksheet part
|
|
155
|
+
WORKSHEET_PN = "worksheets/sheet%d.xml"
|
|
156
|
+
|
|
157
|
+
# worksheet rels part
|
|
158
|
+
WORKSHEET_RELS_PN = "worksheets/_rels/sheet%d.xml.rels"
|
|
159
|
+
|
|
160
|
+
# drawing part
|
|
161
|
+
DRAWING_PN = "drawings/drawing%d.xml"
|
|
162
|
+
|
|
163
|
+
# drawing rels part
|
|
164
|
+
DRAWING_RELS_PN = "drawings/_rels/drawing%d.xml.rels"
|
|
165
|
+
|
|
166
|
+
# chart part
|
|
167
|
+
CHART_PN = "charts/chart%d.xml"
|
|
168
|
+
|
|
169
|
+
# chart part
|
|
170
|
+
IMAGE_PN = "media/image%d.%s"
|
|
171
|
+
|
|
172
|
+
# App validation schema
|
|
173
|
+
APP_XSD = "lib/schema/shared-documentPropertiesExtended.xsd"
|
|
174
|
+
|
|
175
|
+
# core validation schema
|
|
176
|
+
CORE_XSD = "lib/schema/opc-coreProperties.xsd"
|
|
177
|
+
|
|
178
|
+
# content types validation schema
|
|
179
|
+
CONTENT_TYPES_XSD = "lib/schema/opc-contentTypes.xsd"
|
|
180
|
+
|
|
181
|
+
# rels validation schema
|
|
182
|
+
RELS_XSD = "lib/schema/opc-relationships.xsd"
|
|
183
|
+
|
|
184
|
+
# spreadsheetML validation schema
|
|
185
|
+
SML_XSD = "lib/schema/sml.xsd"
|
|
186
|
+
|
|
187
|
+
# drawing validation schema
|
|
188
|
+
DRAWING_XSD = "lib/schema/dml-spreadsheetDrawing.xsd"
|
|
189
|
+
|
|
190
|
+
# number format id for pecentage formatting using the default formatting id.
|
|
191
|
+
NUM_FMT_PERCENT = 9
|
|
192
|
+
|
|
193
|
+
# number format id for date format like 2011/11/13
|
|
194
|
+
NUM_FMT_YYYYMMDD = 100
|
|
195
|
+
|
|
196
|
+
# number format id for time format the creates 2011/11/13 12:23:10
|
|
197
|
+
NUM_FMT_YYYYMMDDHHMMSS = 101
|
|
198
|
+
|
|
199
|
+
# cellXfs id for thin borders around the cell
|
|
200
|
+
STYLE_THIN_BORDER = 1
|
|
201
|
+
|
|
202
|
+
# cellXfs id for default date styling
|
|
203
|
+
STYLE_DATE = 2
|
|
204
|
+
|
|
205
|
+
# error messages RestrictionValidor
|
|
206
|
+
ERR_RESTRICTION = "Invalid Data: %s. %s must be one of %s."
|
|
207
|
+
|
|
208
|
+
# error message DataTypeValidator
|
|
209
|
+
ERR_TYPE = "Invalid Data %s for %s. must be %s."
|
|
210
|
+
|
|
211
|
+
# error message for RegexValidator
|
|
212
|
+
ERR_REGEX = "Invalid Data. %s does not match %s."
|
|
213
|
+
|
|
214
|
+
# error message for duplicate sheet names
|
|
215
|
+
ERR_DUPLICATE_SHEET_NAME = "There is already a worksheet in this workbook named '%s'. Please use a unique name"
|
|
216
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require 'digest'
|
|
2
|
+
require 'base64'
|
|
3
|
+
require 'openssl'
|
|
4
|
+
module Axlsx
|
|
5
|
+
class MsOffCrypto
|
|
6
|
+
|
|
7
|
+
attr_reader :verifier
|
|
8
|
+
attr_reader :key
|
|
9
|
+
|
|
10
|
+
def initialize(password = "passowrd")
|
|
11
|
+
@password = password
|
|
12
|
+
@salt_size = 0x10
|
|
13
|
+
@key_size = 0x100
|
|
14
|
+
@verifier = rand(16**16).to_s
|
|
15
|
+
|
|
16
|
+
#fixed salt for testing
|
|
17
|
+
@salt = [0x90,0xAC,0x68,0x0E,0x76,0xF9,0x43,0x2B,0x8D,0x13,0xB7,0x1D,0xB7,0xC0,0xFC,0x0D].join
|
|
18
|
+
# @salt =Digest::SHA1.digest(rand(16**16).to_s)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def encryption_info
|
|
22
|
+
# v.major v.minor flags header length flags size # AES 128 bit
|
|
23
|
+
header = [3, 0, 2, 0, 0x24, 0, 0, 0, 0xA4, 0, 0, 0, 0x24, 0, 0, 0, 0, 0, 0, 0, 0x0E, 0x66, 0, 0]
|
|
24
|
+
header.concat [0x04, 0x80, 0, 0, 0x80, 0, 0, 0, 0x18, 0, 0, 0, 0xA0, 0xC7, 0xDC, 0x2, 0, 0, 0, 0]
|
|
25
|
+
header.concat "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)".bytes.to_a.pack('s*').bytes.to_a
|
|
26
|
+
header.concat [0, 0]
|
|
27
|
+
header.concat [0x10, 0, 0, 0]
|
|
28
|
+
header.concat [0x90,0xAC,0x68,0x0E,0x76,0xF9,0x43,0x2B,0x8D,0x13,0xB7,0x1D,0xB7,0xC0,0xFC,0x0D]
|
|
29
|
+
header.concat encrypted_verifier.bytes.to_a.pack('c*').bytes.to_a
|
|
30
|
+
header.concat [20, 0,0,0]
|
|
31
|
+
header.concat encrypted_verifier_hash.bytes.to_a.pack('c*').bytes.to_a
|
|
32
|
+
header.flatten!
|
|
33
|
+
header.pack('c*')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def encryption_verifier
|
|
37
|
+
{:salt_size => @salt_size,
|
|
38
|
+
:salt => @salt,
|
|
39
|
+
:encrypted_verifier => encrypted_verifier,
|
|
40
|
+
:varifier_hash_size => 0x14,
|
|
41
|
+
:encrypted_verifier_hash => encrypted_verifier_hash}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# 2.3.3
|
|
45
|
+
def encrypted_verifier
|
|
46
|
+
@encrypted_verifier ||= encrypt(@verifier)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# 2.3.3
|
|
50
|
+
def encrypted_verifier_hash
|
|
51
|
+
verifier_hash = Digest::SHA1.digest(@verifier)
|
|
52
|
+
verifier_hash << Array.new(32 - verifier_hash.size, 0).join('')
|
|
53
|
+
@encrypted_verifier_hash ||= encrypt(verifier_hash)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# 2.3.4.7 ECMA-376 Document Encryption Key Generation (Standard Encryption)
|
|
57
|
+
def key
|
|
58
|
+
sha = Digest::SHA1.new() << (@salt + @password)
|
|
59
|
+
(0..49999).each { |i| sha.update(i.to_s+sha.to_s) }
|
|
60
|
+
key = sha.update(sha.to_s+'0').digest
|
|
61
|
+
a = key.bytes.each_with_index.map { |item, i| 0x36 ^ item }
|
|
62
|
+
x1 = Digest::SHA1.digest((a.concat Array.new(64 - key.size, 0x36)).to_s)
|
|
63
|
+
a = key.bytes.each_with_index.map { |item, i| 0x5C ^ item }
|
|
64
|
+
x2 = Digest::SHA1.digest( (a.concat Array.new(64 - key.size, 0x5C) ).to_s)
|
|
65
|
+
x3 = x1 + x2
|
|
66
|
+
@key ||= x3.bytes.to_a[(0..31)].pack('c*')
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def verify_password
|
|
70
|
+
puts decrypt(@encrypted_verifier)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def encrypt(data)
|
|
74
|
+
aes = OpenSSL::Cipher.new("AES-128-ECB")
|
|
75
|
+
aes.encrypt
|
|
76
|
+
aes.key = key
|
|
77
|
+
aes.update(data)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def decrypt(data)
|
|
81
|
+
aes = OpenSSL::Cipher.new("AES-128-ECB")
|
|
82
|
+
aes.decrypt
|
|
83
|
+
aes.key = key
|
|
84
|
+
aes.update(data)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Axlsx
|
|
2
|
+
# The Parser module mixes in a number of methods to help in generating a model from xml
|
|
3
|
+
# This module is not included in the axlsx library at this time. It is for future development only,
|
|
4
|
+
module Parser
|
|
5
|
+
|
|
6
|
+
# The xml to be parsed
|
|
7
|
+
attr_accessor :parser_xml
|
|
8
|
+
|
|
9
|
+
# parse and assign string attribute
|
|
10
|
+
def parse_string attr_name, xpath
|
|
11
|
+
send("#{attr_name.to_s}=", parse_value(xpath))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# parse convert and assign node text to symbol
|
|
15
|
+
def parse_symbol attr_name, xpath
|
|
16
|
+
v = parse_value xpath
|
|
17
|
+
v = v.to_sym unless v.nil?
|
|
18
|
+
send("#{attr_name.to_s}=", v)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# parse, convert and assign note text to integer
|
|
22
|
+
def parse_integer attr_name, xpath
|
|
23
|
+
v = parse_value xpath
|
|
24
|
+
v = v.to_i if v.respond_to?(:to_i)
|
|
25
|
+
send("#{attr_name.to_s}=", v)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# parse, convert and assign node text to float
|
|
29
|
+
def parse_float attr_name, xpath
|
|
30
|
+
v = parse_value xpath
|
|
31
|
+
v = v.to_f if v.respond_to?(:to_f)
|
|
32
|
+
send("#{attr_name.to_s}=", v)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# return node text based on xpath
|
|
36
|
+
def parse_value xpath
|
|
37
|
+
node = parser_xml.xpath(xpath)
|
|
38
|
+
return nil if node.empty?
|
|
39
|
+
node.text.strip
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
module Axlsx
|
|
2
|
+
# A SimpleTypedList is a type restrictive collection that allows some of the methods from Array and supports basic xml serialization.
|
|
3
|
+
# @private
|
|
4
|
+
class SimpleTypedList
|
|
5
|
+
# The class constants of allowed types
|
|
6
|
+
# @return [Array]
|
|
7
|
+
attr_reader :allowed_types
|
|
8
|
+
|
|
9
|
+
# The index below which items cannot be removed
|
|
10
|
+
# @return [Integer]
|
|
11
|
+
attr_reader :locked_at
|
|
12
|
+
|
|
13
|
+
# The tag name to use when serializing this object
|
|
14
|
+
# by default the parent node for all items in the list is the classname of the first allowed type with the first letter in lowercase.
|
|
15
|
+
# @return [String]
|
|
16
|
+
attr_reader :serialize_as
|
|
17
|
+
|
|
18
|
+
# Creats a new typed list
|
|
19
|
+
# @param [Array, Class] type An array of Class objects or a single Class object
|
|
20
|
+
# @param [String] serialize The tag name to use in serialization
|
|
21
|
+
# @raise [ArgumentError] if all members of type are not Class objects
|
|
22
|
+
def initialize type, serialize_as=nil
|
|
23
|
+
if type.is_a? Array
|
|
24
|
+
type.each { |item| raise ArgumentError, "All members of type must be Class objects" unless item.is_a? Class }
|
|
25
|
+
@allowed_types = type
|
|
26
|
+
else
|
|
27
|
+
raise ArgumentError, "Type must be a Class object or array of Class objects" unless type.is_a? Class
|
|
28
|
+
@allowed_types = [type]
|
|
29
|
+
end
|
|
30
|
+
@list = []
|
|
31
|
+
@locked_at = nil
|
|
32
|
+
@serialize_as = serialize_as
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Lock this list at the current size
|
|
36
|
+
# @return [self]
|
|
37
|
+
def lock
|
|
38
|
+
@locked_at = @list.size
|
|
39
|
+
self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def to_ary
|
|
43
|
+
@list
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Unlock the list
|
|
47
|
+
# @return [self]
|
|
48
|
+
def unlock
|
|
49
|
+
@locked_at = nil
|
|
50
|
+
self
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Concat operator
|
|
54
|
+
# @param [Any] v the data to be added
|
|
55
|
+
# @raise [ArgumentError] if the value being added is not one fo the allowed types
|
|
56
|
+
# @return [Integer] returns the index of the item added.
|
|
57
|
+
def <<(v)
|
|
58
|
+
DataTypeValidator.validate "SimpleTypedList.<<", @allowed_types, v
|
|
59
|
+
@list << v
|
|
60
|
+
@list.size - 1
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# alternate of << method
|
|
64
|
+
# @see <<
|
|
65
|
+
def push(v)
|
|
66
|
+
self.<< v
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# delete the item from the list
|
|
70
|
+
# @param [Any] v The item to be deleted.
|
|
71
|
+
# @raise [ArgumentError] if the item's index is protected by locking
|
|
72
|
+
# @return [Any] The item deleted
|
|
73
|
+
def delete(v)
|
|
74
|
+
return unless @list.include? v
|
|
75
|
+
raise ArgumentError, "Item is protected and cannot be deleted" if protected? @list.index(v)
|
|
76
|
+
@list.delete v
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# delete the item from the list at the index position provided
|
|
80
|
+
# @raise [ArgumentError] if the index is protected by locking
|
|
81
|
+
# @return [Any] The item deleted
|
|
82
|
+
def delete_at(index)
|
|
83
|
+
@list[index]
|
|
84
|
+
raise ArgumentError, "Item is protected and cannot be deleted" if protected? index
|
|
85
|
+
@list.delete_at index
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# positional assignment. Adds the item at the index specified
|
|
89
|
+
# @param [Integer] index
|
|
90
|
+
# @param [Any] v
|
|
91
|
+
# @raise [ArgumentError] if the index is protected by locking
|
|
92
|
+
# @raise [ArgumentError] if the item is not one of the allowed types
|
|
93
|
+
def []=(index, v)
|
|
94
|
+
DataTypeValidator.validate "SimpleTypedList.<<", @allowed_types, v
|
|
95
|
+
raise ArgumentError, "Item is protected and cannot be changed" if protected? index
|
|
96
|
+
@list[index] = v
|
|
97
|
+
v
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# determines if the index is protected
|
|
101
|
+
# @param [Integer] index
|
|
102
|
+
def protected? index
|
|
103
|
+
return false unless @locked_at.is_a? Fixnum
|
|
104
|
+
index < @locked_at
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# override the equality method so that this object can be compared to a simple array.
|
|
108
|
+
# if this object's list is equal to the specifiec array, we return true.
|
|
109
|
+
def ==(v)
|
|
110
|
+
v == @list
|
|
111
|
+
end
|
|
112
|
+
# method_mission override to pass allowed methods to the list.
|
|
113
|
+
# @note
|
|
114
|
+
# the following methods are not allowed
|
|
115
|
+
# :replace
|
|
116
|
+
# :insert
|
|
117
|
+
# :collect!
|
|
118
|
+
# :map!
|
|
119
|
+
# :pop
|
|
120
|
+
# :delete_if
|
|
121
|
+
# :reverse!
|
|
122
|
+
# :shift
|
|
123
|
+
# :shuffle!
|
|
124
|
+
# :slice!
|
|
125
|
+
# :sort!
|
|
126
|
+
# :uniq!
|
|
127
|
+
# :unshift
|
|
128
|
+
# :zip
|
|
129
|
+
# :flatten!
|
|
130
|
+
# :fill
|
|
131
|
+
# :drop
|
|
132
|
+
# :drop_while
|
|
133
|
+
# :delete_if
|
|
134
|
+
# :clear
|
|
135
|
+
def method_missing(meth, *args, &block)
|
|
136
|
+
raise ArgumentError, "#{meth} not supported" if [:replace, :insert, :collect!, :map!, :pop, :delete_if, :reverse!, :shift, :shuffle!, :slice!, :sort!, :uniq!, :unshift, :zip, :flatten!, :fill, :drop, :drop_while, :delete_if, :clear].include? meth.to_sym
|
|
137
|
+
if @list.respond_to? meth
|
|
138
|
+
@list.send(meth, *args, &block)
|
|
139
|
+
else
|
|
140
|
+
puts "method:#{meth.inspect}"
|
|
141
|
+
super
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Serializes the list
|
|
146
|
+
# If the serialize_as property is set, it is used as the parent node name.
|
|
147
|
+
# If the serialize_as property is nil, the first item in the list of allowed_types will be used, having the first letter of the class changed to lower case.
|
|
148
|
+
# @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
|
|
149
|
+
# @return [String]
|
|
150
|
+
def to_xml(xml)
|
|
151
|
+
classname = @allowed_types[0].name.split('::').last
|
|
152
|
+
el_name = serialize_as || (classname[0,1].downcase + classname[1..-1]).pluralize
|
|
153
|
+
xml.send(el_name, :count=>@list.size) {
|
|
154
|
+
@list.each { |item| item.to_xml(xml) }
|
|
155
|
+
}
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
module Axlsx
|
|
2
|
+
# Validate a value against a specific list of allowed values.
|
|
3
|
+
class RestrictionValidator
|
|
4
|
+
# Perform validation
|
|
5
|
+
# @param [String] name The name of what is being validatied. This is included in the error message
|
|
6
|
+
# @param [Array] choices The list of choices to validate against
|
|
7
|
+
# @param [Any] v The value to be validated
|
|
8
|
+
# @raise [ArgumentError] Raised if the value provided is not in the list of choices.
|
|
9
|
+
# @return [Boolean] true if validation succeeds.
|
|
10
|
+
def self.validate(name, choices, v)
|
|
11
|
+
raise ArgumentError, (ERR_RESTRICTION % [v.to_s, name, choices.inspect]) unless choices.include?(v)
|
|
12
|
+
true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Validates the value against the regular expression provided.
|
|
17
|
+
class RegexValidator
|
|
18
|
+
# @param [String] name The name of what is being validated. This is included in the output when the value is invalid
|
|
19
|
+
# @param [Regexp] regex The regular expression to evaluate
|
|
20
|
+
# @param [Any] v The value to validate.
|
|
21
|
+
def self.validate(name, regex, v)
|
|
22
|
+
raise ArgumentError, (ERR_REGEX % [v.inspect, regex.to_s]) unless (v.respond_to?(:=~) && v =~ regex)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
# Validate that the class of the value provided is either an instance or the class of the allowed types and that any specified additional validation returns true.
|
|
26
|
+
class DataTypeValidator
|
|
27
|
+
# Perform validation
|
|
28
|
+
# @param [String] name The name of what is being validated. This is included in the error message
|
|
29
|
+
# @param [Array, Class] types A single class or array of classes that the value is validated against.
|
|
30
|
+
# @param [Block] other Any block that must evaluate to true for the value to be valid
|
|
31
|
+
# @raise [ArugumentError] Raised if the class of the value provided is not in the specified array of types or the block passed returns false
|
|
32
|
+
# @return [Boolean] true if validation succeeds.
|
|
33
|
+
# @see validate_boolean
|
|
34
|
+
def self.validate(name, types, v, other= lambda{|arg| true })
|
|
35
|
+
types = [types] unless types.is_a? Array
|
|
36
|
+
valid_type = false
|
|
37
|
+
if v.class == Class
|
|
38
|
+
types.each { |t| valid_type = true if v.ancestors.include?(t) }
|
|
39
|
+
else
|
|
40
|
+
types.each { |t| valid_type = true if v.is_a?(t) }
|
|
41
|
+
end
|
|
42
|
+
raise ArgumentError, (ERR_TYPE % [v.inspect, name, types.inspect]) unless (other.call(v) && valid_type)
|
|
43
|
+
end
|
|
44
|
+
true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Requires that the value is a Fixnum or Integer and is greater or equal to 0
|
|
48
|
+
# @param [Any] v The value validated
|
|
49
|
+
# @raise [ArgumentError] raised if the value is not a Fixnum or Integer value greater or equal to 0
|
|
50
|
+
# @return [Boolean] true if the data is valid
|
|
51
|
+
def self.validate_unsigned_int(v)
|
|
52
|
+
DataTypeValidator.validate(:unsigned_int, [Fixnum, Integer], v, lambda { |arg| arg.respond_to?(:>=) && arg >= 0 })
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Requires that the value is a Fixnum or Integer
|
|
56
|
+
# @param [Any] v The value validated
|
|
57
|
+
def self.validate_int(v)
|
|
58
|
+
DataTypeValidator.validate :unsigned_int, [Fixnum, Integer], v
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Requires that the value is a form that can be evaluated as a boolean in an xml document.
|
|
62
|
+
# The value must be an instance of Fixnum, String, Integer, Symbol, TrueClass or FalseClass and
|
|
63
|
+
# it must be one of 0, 1, "true", "false", :true, :false, true, false, "0", or "1"
|
|
64
|
+
# @param [Any] v The value validated
|
|
65
|
+
def self.validate_boolean(v)
|
|
66
|
+
DataTypeValidator.validate(:boolean, [Fixnum, String, Integer, Symbol, TrueClass, FalseClass], v, lambda { |arg| [0, 1, "true", "false", :true, :false, true, false, "0", "1"].include?(arg) })
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Requires that the value is a String
|
|
70
|
+
# @param [Any] v The value validated
|
|
71
|
+
def self.validate_string(v)
|
|
72
|
+
DataTypeValidator.validate :string, String, v
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Requires that the value is a Float
|
|
76
|
+
# @param [Any] v The value validated
|
|
77
|
+
def self.validate_float(v)
|
|
78
|
+
DataTypeValidator.validate :float, Float, v
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Requires that the value is valid pattern type.
|
|
82
|
+
# valid pattern types must be one of :none, :solid, :mediumGray, :darkGray, :lightGray, :darkHorizontal, :darkVertical, :darkDown,
|
|
83
|
+
# :darkUp, :darkGrid, :darkTrellis, :lightHorizontal, :lightVertical, :lightDown, :lightUp, :lightGrid, :lightTrellis, :gray125, or :gray0625.
|
|
84
|
+
# @param [Any] v The value validated
|
|
85
|
+
def self.validate_pattern_type(v)
|
|
86
|
+
RestrictionValidator.validate :pattern_type, [:none, :solid, :mediumGray, :darkGray, :lightGray, :darkHorizontal, :darkVertical, :darkDown, :darkUp, :darkGrid,
|
|
87
|
+
:darkTrellis, :lightHorizontal, :lightVertical, :lightDown, :lightUp, :lightGrid, :lightTrellis, :gray125, :gray0625], v
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Requires that the value is a gradient_type.
|
|
91
|
+
# valid types are :linear and :path
|
|
92
|
+
# @param [Any] v The value validated
|
|
93
|
+
def self.validate_gradient_type(v)
|
|
94
|
+
RestrictionValidator.validate :gradient_type, [:linear, :path], v
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Requires that the value is a valid horizontal_alignment
|
|
98
|
+
# :general, :left, :center, :right, :fill, :justify, :centerContinuous, :distributed are allowed
|
|
99
|
+
# @param [Any] v The value validated
|
|
100
|
+
def self.validate_horizontal_alignment(v)
|
|
101
|
+
RestrictionValidator.validate :horizontal_alignment, [:general, :left, :center, :right, :fill, :justify, :centerContinuous, :distributed], v
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Requires that the value is a valid vertical_alignment
|
|
105
|
+
# :top, :center, :bottom, :justify, :distributed are allowed
|
|
106
|
+
# @param [Any] v The value validated
|
|
107
|
+
def self.validate_vertical_alignment(v)
|
|
108
|
+
RestrictionValidator.validate :vertical_alignment, [:top, :center, :bottom, :justify, :distributed], v
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Requires that the value is a valid content_type
|
|
112
|
+
# TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, DRAWING_CT are allowed
|
|
113
|
+
# @param [Any] v The value validated
|
|
114
|
+
def self.validate_content_type(v)
|
|
115
|
+
RestrictionValidator.validate :content_type, [TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, JPEG_CT, GIF_CT, PNG_CT, DRAWING_CT], v
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Requires that the value is a valid relationship_type
|
|
119
|
+
# XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R are allowed
|
|
120
|
+
# @param [Any] v The value validated
|
|
121
|
+
def self.validate_relationship_type(v)
|
|
122
|
+
RestrictionValidator.validate :relationship_type, [XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R, IMAGE_R], v
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Requires that the value is a valid table element type
|
|
126
|
+
# :wholeTable, :headerRow, :totalRow, :firstColumn, :lastColumn, :firstRowStripe, :secondRowStripe, :firstColumnStripe, :secondColumnStripe, :firstHeaderCell, :lastHeaderCell, :firstTotalCell, :lastTotalCell, :firstSubtotalColumn, :secondSubtotalColumn, :thirdSubtotalColumn, :firstSubtotalRow, :secondSubtotalRow, :thirdSubtotalRow, :blankRow, :firstColumnSubheading, :secondColumnSubheading, :thirdColumnSubheading, :firstRowSubheading, :secondRowSubheading, :thirdRowSubheading, :pageFieldLabels, :pageFieldValues are allowed
|
|
127
|
+
# @param [Any] v The value validated
|
|
128
|
+
def self.validate_table_element_type(v)
|
|
129
|
+
RestrictionValidator.validate :table_element_type, [:wholeTable, :headerRow, :totalRow, :firstColumn, :lastColumn, :firstRowStripe, :secondRowStripe, :firstColumnStripe, :secondColumnStripe, :firstHeaderCell, :lastHeaderCell, :firstTotalCell, :lastTotalCell, :firstSubtotalColumn, :secondSubtotalColumn, :thirdSubtotalColumn, :firstSubtotalRow, :secondSubtotalRow, :thirdSubtotalRow, :blankRow, :firstColumnSubheading, :secondColumnSubheading, :thirdColumnSubheading, :firstRowSubheading, :secondRowSubheading, :thirdRowSubheading, :pageFieldLabels, :pageFieldValues], v
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
end
|