axlsx 1.0.15 → 1.0.16

Sign up to get free protection for your applications and to get access to all the features.
@@ -47,7 +47,24 @@ module Axlsx
47
47
  yield self if block_given?
48
48
  @picture_locking = PictureLocking.new(options)
49
49
  end
50
+
51
+ attr_reader :hyperlink
50
52
 
53
+ # sets or updates a hyperlink for this image.
54
+ # @param [String] v The href value for the hyper link
55
+ # @option options @see Hyperlink#initialize All options available to the Hyperlink class apply - however href will be overridden with the v parameter value.
56
+ def hyperlink=(v, options={})
57
+ options[:href] = v
58
+ if @hyperlink.is_a?(Hyperlink)
59
+ options.each do |o|
60
+ @hyperlink.send("#{o[0]}=", o[1]) if @hyperlink.respond_to? "#{o[0]}="
61
+ end
62
+ else
63
+ @hyperlink = Hyperlink.new(self, options)
64
+ end
65
+ @hyperlink
66
+ end
67
+
51
68
  def image_src=(v)
52
69
  Axlsx::validate_string(v)
53
70
  RestrictionValidator.validate 'Pic.image_src', ALLOWED_EXTENSIONS, File.extname(v).delete('.')
@@ -75,7 +92,7 @@ module Axlsx
75
92
  end
76
93
 
77
94
  # The index of this image in the workbooks images collections
78
- # @return [Index]
95
+ # @return [Index]
79
96
  def index
80
97
  @anchor.drawing.worksheet.workbook.images.index(self)
81
98
  end
@@ -86,6 +103,11 @@ module Axlsx
86
103
  "#{IMAGE_PN % [(index+1), extname]}"
87
104
  end
88
105
 
106
+ # The relational id withing the drawing's relationships
107
+ def id
108
+ @anchor.drawing.charts.size + @anchor.drawing.images.index(self) + 1
109
+ end
110
+
89
111
  # providing access to the anchor's width attribute
90
112
  # @param [Integer] v
91
113
  # @see OneCellAnchor.width
@@ -127,13 +149,17 @@ module Axlsx
127
149
  def to_xml(xml)
128
150
  xml.pic {
129
151
  xml.nvPicPr {
130
- xml.cNvPr :id=>"2", :name=>name, :descr=>descr
152
+ xml.cNvPr(:id=>"2", :name=>name, :descr=>descr) {
153
+ if @hyperlink.is_a?(Hyperlink)
154
+ @hyperlink.to_xml(xml)
155
+ end
156
+ }
131
157
  xml.cNvPicPr {
132
158
  picture_locking.to_xml(xml)
133
159
  }
134
160
  }
135
161
  xml.blipFill {
136
- xml[:a].blip :'xmlns:r' => XML_NS_R, :'r:embed'=>"rId1"
162
+ xml[:a].blip :'xmlns:r' => XML_NS_R, :'r:embed'=>"rId#{id}"
137
163
  xml[:a].stretch {
138
164
  xml.fillRect
139
165
  }
@@ -89,10 +89,10 @@ module Axlsx
89
89
  end
90
90
 
91
91
  # Encrypt the package into a CFB using the password provided
92
- # def encrypt(file_name, password)
93
- # moc = MsOffCrypto.new(file_name, password)
94
- # moc.save
95
- # end
92
+ def encrypt(file_name, password)
93
+ moc = MsOffCrypto.new(file_name, password)
94
+ moc.save
95
+ end
96
96
 
97
97
  # Validate all parts of the package against xsd schema.
98
98
  # @return [Array] An array of all validation errors found.
@@ -21,9 +21,21 @@ module Axlsx
21
21
  # @see DRAWING_R
22
22
  # @return [String]
23
23
  attr_reader :Type
24
- def initialize(type, target)
24
+
25
+ # The target mode of the relationship
26
+ # used for hyperlink type relationships to mark the relationship to an external resource
27
+ # TargetMode can be specified during initialization by passing in a :target_mode option
28
+ # Target mode must be :external for now.
29
+ attr_reader :TargetMode
30
+
31
+ # creates a new relationship
32
+ # @param [String] Type The type of the relationship
33
+ # @param [String] Target The target for the relationship
34
+ # @option [Symbol] target_mode only accepts :external.
35
+ def initialize(type, target, options={})
25
36
  self.Target=target
26
37
  self.Type=type
38
+ self.TargetMode = options.delete(:target_mode) if options[:target_mode]
27
39
  end
28
40
 
29
41
  # @see Target
@@ -31,6 +43,9 @@ module Axlsx
31
43
  # @see Type
32
44
  def Type=(v) Axlsx::validate_relationship_type v; @Type = v end
33
45
 
46
+ # @see TargetMode
47
+ def TargetMode=(v) RestrictionValidator.validate 'Relationship.TargetMode', [:External, :Internal], v; @TargetMode = v; end
48
+
34
49
  # Serializes the relationship
35
50
  # @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
36
51
  # @param [String] rId the reference id of the object.
@@ -18,7 +18,7 @@ require 'axlsx/rels/relationship.rb'
18
18
  each_with_index { |rel, index| rel.to_xml(xml, "rId#{index+1}") }
19
19
  }
20
20
  end
21
- builder.to_xml
21
+ builder.to_xml(:save_with => 0)
22
22
  end
23
23
 
24
24
  end
@@ -246,7 +246,7 @@ module Axlsx
246
246
  end
247
247
  }
248
248
  end
249
- builder.to_xml
249
+ builder.to_xml(:save_with => 0)
250
250
  end
251
251
 
252
252
  private
@@ -10,13 +10,13 @@ module Axlsx
10
10
  VERSION_PACKING = 'l s30 l3'
11
11
 
12
12
  # The serialization for the MS-OFF-CRYPTO dataspace map stream
13
- DATA_SPACE_MAP_PACKING = 'l6 s16 l s25'
13
+ DATA_SPACE_MAP_PACKING = 'l6 s16 l s25 x2'
14
14
 
15
15
  # The serialization for the MS-OFF-CRYPTO strong encrytion data space stream
16
- STRONG_ENCRYPTION_DATA_SPACE_PACKING = 'l3 s25'
16
+ STRONG_ENCRYPTION_DATA_SPACE_PACKING = 'l3 s26'
17
17
 
18
18
  # The serialization for the MS-OFF-CRYPTO primary stream
19
- PRIMARY_PACKING = 'l3 s38 l s39 l3 x12 l x2'
19
+ PRIMARY_PACKING = 'l3 s38 l s40 l3 x12 l'
20
20
 
21
21
  # The cutoff size that determines if a stream should be in the mini-fat or the fat
22
22
  MINI_CUTOFF = 4096
@@ -28,6 +28,7 @@ module Axlsx
28
28
  # @param [MsOffCrypto] ms_off_crypto
29
29
  def initialize(ms_off_crypto)
30
30
  @file_name = ms_off_crypto.file_name
31
+ @ms_off_crypto = ms_off_crypto
31
32
  create_storages
32
33
  mini_fat_stream
33
34
  mini_fat
@@ -95,6 +96,17 @@ module Axlsx
95
96
  @header ||= create_header
96
97
  end
97
98
 
99
+ # returns the encryption info from the ms_off_crypt object provided during intialization
100
+ # @return [String] encryption info
101
+ def encryption_info
102
+ @ms_off_crypto.encryption_info
103
+ end
104
+
105
+ # returns the encrypted package from the ms_off_crypt object provided during initalization
106
+ # @return [String] encrypted package
107
+ def encrypted_package
108
+ @ms_off_crypto.encrypted_package
109
+ end
98
110
 
99
111
  # writes the compound binary file to disk
100
112
  def save
@@ -114,12 +126,11 @@ module Axlsx
114
126
  # Generates the storages required for ms-office-cryptography cfb
115
127
  def create_storages
116
128
  @storages = []
117
- @encryption_info = ms_off_crypto.encryption_info
118
- @encrypted_package = ms_off_crypto.encrypted_package
119
- @storages << Storage.new('R', :type=>Storage::TYPES[:root], :color=>Storage::COLORS[:red], :child=>1, :modified=>129685612742510730)
120
- @storages.last.name_size = 2
121
- @storages << Storage.new('EncryptionInfo', :data=>@encryption_info, :left=>3, :size => @encryption_info.size) # example shows right child. do we need the summary info????
122
- @storages << Storage.new('EncryptedPackage', :data=>@encrypted_package, :color=>Storage::COLORS[:red], :size=>@encrypted_package.size)
129
+ @encryption_info = @ms_off_crypto.encryption_info
130
+ @encrypted_package = @ms_off_crypto.encrypted_package
131
+
132
+ @storages << Storage.new('EncryptionInfo', :data=>encryption_info, :left=>3, :right=>11) # example shows right child. do we need the summary info????
133
+ @storages << Storage.new('EncryptedPackage', :data=>encrypted_package, :color=>Storage::COLORS[:red])
123
134
  @storages << Storage.new([6].pack("c")+"DataSpaces", :child=>5, :modified =>129685612740945580, :created=>129685612740819979)
124
135
  @storages << version
125
136
  @storages << data_space_map
@@ -128,6 +139,12 @@ module Axlsx
128
139
  @storages << Storage.new('TransformInfo', :color => Storage::COLORS[:red], :child=>9, :created=>129685612740834130, :modified=>129685612740943959)
129
140
  @storages << Storage.new('StrongEncryptionTransform', :child=>10, :created=>129685612740834169, :modified=>129685612740942280)
130
141
  @storages << primary
142
+ # @storages << summary_information
143
+ # @storages << document_summary_information
144
+
145
+ # we do this at the end as we need to build the minifat stream to determine the size. #HOWEVER - it looks like the size should not include the padding?
146
+ @storages.unshift Storage.new('Root Entry', :type=>Storage::TYPES[:root], :color=>Storage::COLORS[:red], :child=>1, :data => mini_fat_stream)
147
+
131
148
  end
132
149
 
133
150
  # generates the mini fat stream
@@ -135,10 +152,11 @@ module Axlsx
135
152
  def create_mini_fat_stream
136
153
  mfs = []
137
154
  @storages.select{ |s| s.type == Storage::TYPES[:stream] && s.size < MINI_CUTOFF}.each_with_index do |stream, index|
155
+ puts "#{stream.name.pack('c*')}: #{stream.data.size}"
138
156
  mfs.concat stream.data
139
- mfs.concat Array.new(64 - (mfs.size % 64), 0) if mfs.size % 64
157
+ mfs.concat Array.new(64 - (mfs.size % 64), 0) if mfs.size % 64 > 0
158
+ puts "mini fat stream size: #{mfs.size}"
140
159
  end
141
- @storages[0].size = mfs.size
142
160
  mfs.concat(Array.new(512 - (mfs.size % 512), 0))
143
161
  mfs.pack 'c*'
144
162
  end
@@ -149,7 +167,7 @@ module Axlsx
149
167
  mfs = []
150
168
  @storages.select{ |s| s.type == Storage::TYPES[:stream] && s.size >= MINI_CUTOFF}.each_with_index do |stream, index|
151
169
  mfs.concat stream.data
152
- mfs.concat Array.new(512 - (mfs.size % 512), 0) if mfs.size % 512
170
+ mfs.concat Array.new(512 - (mfs.size % 512), 0) if mfs.size % 512 > 0
153
171
  end
154
172
  mfs.pack 'c*'
155
173
  end
@@ -214,7 +232,7 @@ module Axlsx
214
232
  # creates the stron encryption data space storage
215
233
  # @return [Storgae]
216
234
  def create_strong_encryption_data_space
217
- v_stream = [8,1,50,"StrongEncryptionTransform".bytes.to_a].flatten.pack STRONG_ENCRYPTION_DATA_SPACE_PACKING
235
+ v_stream = [8,1,50,"StrongEncryptionTransform".bytes.to_a,0].flatten.pack STRONG_ENCRYPTION_DATA_SPACE_PACKING
218
236
  Storage.new("StrongEncryptionDataSpace", :data=>v_stream, :size => v_stream.size)
219
237
  end
220
238
 
@@ -222,41 +240,58 @@ module Axlsx
222
240
  # @return [Storgae]
223
241
  def create_primary
224
242
  v_stream = [88,1,76,"{FF9A3F03-56EF-4613-BDD5-5A41C1D07246}".bytes.to_a].flatten
225
- v_stream.concat [78, "Microsoft.Container.EncryptionTransform".bytes.to_a,1,1,1,4].flatten
243
+ v_stream.concat [78, "Microsoft.Container.EncryptionTransform".bytes.to_a,0,1,1,1,4].flatten
226
244
  v_stream = v_stream.pack PRIMARY_PACKING
227
245
  Storage.new([6].pack("c")+"Primary", :data=>v_stream)
228
246
  end
229
247
 
230
248
 
231
- SUMMARY_INFORMATION_PACKING = ""
232
249
  # creates the summary information storage
233
250
  # @return [Storage]
234
251
  def create_summary_information
235
- # FEFF 0000 030A 0100 0000 0000 0000 0000
236
- # 0000 0000 0000 0000 0100 0000 E085 9FF2
237
- # F94F 6810 AB91 0800 2B27 B3D9 3000 0000
238
- # AC00 0000 0700 0000 0100 0000 4000 0000
239
- # 0400 0000 4800 0000 0800 0000 5800 0000
240
- # 1200 0000 6800 0000 0C00 0000 8C00 0000
241
- # 0D00 0000 9800 0000 1300 0000 A400 0000
242
- # 0200 0000 E9FD 0000 1E00 0000 0800 0000
243
- # 7261 6E64 796D 0000 1E00 0000 0800 0000
244
- # 7261 6E64 796D 0000 1E00 0000 1C00 0000
245
- # 4D69 6372 6F73 6F66 7420 4D61 6369 6E74
246
- # 6F73 6820 4578 6365 6C00 0000 4000 0000
247
- # 10AC 5396 60BC CC01 4000 0000 40F4 FDAF
248
- # 60BC CC01 0300 0000 0100 0000
249
252
  v_stream = []
250
- v_stream = v_stream.pack SUMMARY_INFORMATION_PACKING
251
- Storage.new([5].pack('c')+"SummaryInformation", :data=>v_stream)
253
+ v_stream.concat [0xFEFF, 0x0000, 0x030A, 0x0100, 0x0000, 0x0000, 0x0000, 0x0000]
254
+ v_stream.concat [0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0xE085, 0x9FF2]
255
+ v_stream.concat [0xF94F, 0x6810, 0xAB91, 0x0800, 0x2B27, 0xB3D9, 0x3000, 0x0000]
256
+ v_stream.concat [0xAC00, 0x0000, 0x0700, 0x0000, 0x0100, 0x0000, 0x4000, 0x0000]
257
+ v_stream.concat [0x0400, 0x0000, 0x4800, 0x0000, 0x0800, 0x0000, 0x5800, 0x0000]
258
+ v_stream.concat [0x1200, 0x0000, 0x6800, 0x0000, 0x0C00, 0x0000, 0x8C00, 0x0000]
259
+ v_stream.concat [0x0D00, 0x0000, 0x9800, 0x0000, 0x1300, 0x0000, 0xA400, 0x0000]
260
+ v_stream.concat [0x0200, 0x0000, 0xE9FD, 0x0000, 0x1E00, 0x0000, 0x0800, 0x0000]
261
+ v_stream.concat [0x7261, 0x6E64, 0x796D, 0x0000, 0x1E00, 0x0000, 0x0800, 0x0000]
262
+ v_stream.concat [0x7261, 0x6E64, 0x796D, 0x0000, 0x1E00, 0x0000, 0x1C00, 0x0000]
263
+ v_stream.concat [0x4D69, 0x6372, 0x6F73, 0x6F66, 0x7420, 0x4D61, 0x6369, 0x6E74]
264
+ v_stream.concat [0x6F73, 0x6820, 0x4578, 0x6365, 0x6C00, 0x0000, 0x4000, 0x0000]
265
+ v_stream.concat [0x10AC, 0x5396, 0x60BC, 0xCC01, 0x4000, 0x0000, 0x40F4, 0xFDAF]
266
+ v_stream.concat [0x60BC, 0xCC01, 0x0300, 0x0000, 0x0100, 0x0000]
267
+
268
+ v_stream = v_stream.pack "s*"
269
+
270
+ Storage.new([5].pack('c')+"SummaryInformation", :data=>v_stream, :left => 2)
252
271
  end
253
272
 
254
- DOCUMENT_SUMMARY_INFORMATION_PACKING = ""
273
+
255
274
  # creates the document summary information storage
256
275
  # @return [Storage]
257
276
  def create_document_summary_information
258
277
  v_stream = []
259
- v_stream = v_stream.pack DOCUMENT_SUMMARY_INFORMATION_PACKING
278
+ v_stream.concat [0xFEFF, 0x0000, 0x030A, 0x0100, 0x0000, 0x0000, 0x0000, 0x0000]
279
+ v_stream.concat [0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x02D5, 0xCDD5]
280
+ v_stream.concat [0x9C2E, 0x1B10, 0x9397, 0x0800, 0x2B2C, 0xF9AE, 0x3000, 0x0000]
281
+ v_stream.concat [0xCC00, 0x0000, 0x0900, 0x0000, 0x0100, 0x0000, 0x5000, 0x0000]
282
+ v_stream.concat [0x0F00, 0x0000, 0x5800, 0x0000, 0x1700, 0x0000, 0x6400, 0x0000]
283
+ v_stream.concat [0x0B00, 0x0000, 0x6C00, 0x0000, 0x1000, 0x0000, 0x7400, 0x0000]
284
+ v_stream.concat [0x1300, 0x0000, 0x7C00, 0x0000, 0x1600, 0x0000, 0x8400, 0x0000]
285
+ v_stream.concat [0x0D00, 0x0000, 0x8C00, 0x0000, 0x0C00, 0x0000, 0x9F00, 0x0000]
286
+ v_stream.concat [0x0200, 0x0000, 0xE9FD, 0x0000, 0x1E00, 0x0000, 0x0400, 0x0000]
287
+ v_stream.concat [0x0000, 0x0000, 0x0300, 0x0000, 0x0000, 0x0C00, 0x0B00, 0x0000]
288
+ v_stream.concat [0x0000, 0x0000, 0x0B00, 0x0000, 0x0000, 0x0000, 0x0B00, 0x0000]
289
+ v_stream.concat [0x0000, 0x0000, 0x0B00, 0x0000, 0x0000, 0x0000, 0x1E10, 0x0000]
290
+ v_stream.concat [0x0100, 0x0000, 0x0700, 0x0000, 0x5368, 0x6565, 0x7431, 0x000C]
291
+ v_stream.concat [0x1000, 0x0002, 0x0000, 0x001E, 0x0000, 0x0013, 0x0000, 0x00E3]
292
+ v_stream.concat [0x83AF, 0xE383, 0xBCE3, 0x82AF, 0xE382, 0xB7E3, 0x83BC, 0xE383]
293
+ v_stream.concat [0x8800, 0x0300, 0x0000, 0x0100, 0x0000, 0x0000]
294
+ v_stream = v_stream.pack 'c*'
260
295
  Storage.new([5].pack('c')+"DocumentSummaryInformation", :data=>v_stream)
261
296
  end
262
297
 
@@ -73,6 +73,9 @@ module Axlsx
73
73
  # image rels namespace
74
74
  IMAGE_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
75
75
 
76
+ # image rels namespace
77
+ HYPERLINK_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
78
+
76
79
  # table content type
77
80
  TABLE_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"
78
81
 
@@ -169,23 +172,26 @@ module Axlsx
169
172
  # chart part
170
173
  IMAGE_PN = "media/image%d.%s"
171
174
 
175
+ # location of schema files for validation
176
+ SCHEMA_BASE = File.dirname(__FILE__)+'/../../schema/'
177
+
172
178
  # App validation schema
173
- APP_XSD = "lib/schema/shared-documentPropertiesExtended.xsd"
179
+ APP_XSD = SCHEMA_BASE + "shared-documentPropertiesExtended.xsd"
174
180
 
175
181
  # core validation schema
176
- CORE_XSD = "lib/schema/opc-coreProperties.xsd"
182
+ CORE_XSD = SCHEMA_BASE + "opc-coreProperties.xsd"
177
183
 
178
184
  # content types validation schema
179
- CONTENT_TYPES_XSD = "lib/schema/opc-contentTypes.xsd"
185
+ CONTENT_TYPES_XSD = SCHEMA_BASE + "opc-contentTypes.xsd"
180
186
 
181
187
  # rels validation schema
182
- RELS_XSD = "lib/schema/opc-relationships.xsd"
188
+ RELS_XSD = SCHEMA_BASE + "opc-relationships.xsd"
183
189
 
184
190
  # spreadsheetML validation schema
185
- SML_XSD = "lib/schema/sml.xsd"
191
+ SML_XSD = SCHEMA_BASE + "sml.xsd"
186
192
 
187
193
  # drawing validation schema
188
- DRAWING_XSD = "lib/schema/dml-spreadsheetDrawing.xsd"
194
+ DRAWING_XSD = SCHEMA_BASE + "dml-spreadsheetDrawing.xsd"
189
195
 
190
196
  # number format id for pecentage formatting using the default formatting id.
191
197
  NUM_FMT_PERCENT = 9
@@ -48,7 +48,7 @@ module Axlsx
48
48
  # encrypts and returns the package specified by the file name
49
49
  # @return [String]
50
50
  def encrypted_package
51
- @encrypted_package ||= encrypt_package(file_name, password)
51
+ @encrypted_package ||= encrypt_package(file_name)
52
52
  end
53
53
 
54
54
  # returns the encryption info for this instance of ms-off-crypto
@@ -94,10 +94,10 @@ module Axlsx
94
94
  end
95
95
 
96
96
  # size of unencrypted package? concated with encrypted package
97
- def encrypt_package(file_name, password)
97
+ def encrypt_package(file_name)
98
98
  package = File.open(file_name, 'r')
99
- package_text = package.read
100
- [package_text.bytes.to_a.size].pack('q') + encrypt(package_text)
99
+ crypt_pack = encrypt(package.read)
100
+ [crypt_pack.size].pack('q') + crypt_pack
101
101
  end
102
102
 
103
103
  # Generates an encryption info structure
@@ -106,18 +106,18 @@ module Axlsx
106
106
  header = [3, 0, 2, 0] # version
107
107
  # Header flags copy
108
108
  header.concat [0x24, 0, 0, 0] #flags -- VERY UNSURE ABOUT THIS STILL
109
- header.concat [0, 0, 0, 0] #unused
109
+ # header.concat [0, 0, 0, 0] #unused
110
110
  header.concat [0xA4, 0, 0, 0] #length
111
111
  # Header
112
112
  header.concat [0x24, 0, 0, 0] #flags again
113
- header.concat [0, 0, 0, 0] #unused again,
113
+ # header.concat [0, 0, 0, 0] #unused again,
114
114
  header.concat [0x0E, 0x66, 0, 0] #alg id
115
115
  header.concat [0x04, 0x80, 0, 0] #alg hash id
116
116
  header.concat [key.size, 0, 0, 0] #key size
117
117
  header.concat [0x18, 0, 0, 0] #provider type
118
- header.concat [0, 0, 0, 0] #reserved 1
119
- header.concat [0, 0, 0, 0] #reserved 2
120
- #header.concat [0xA0, 0xC7, 0xDC, 0x2, 0, 0, 0, 0]
118
+ # header.concat [0, 0, 0, 0] #reserved 1
119
+ # header.concat [0, 0, 0, 0] #reserved 2
120
+ header.concat [0xA0, 0xC7, 0xDC, 0x2, 0, 0, 0, 0]
121
121
  header.concat "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)".bytes.to_a.pack('s*').bytes.to_a
122
122
  header.concat [0, 0] #null terminator
123
123
 
@@ -28,7 +28,6 @@ module Axlsx
28
28
  @modified,
29
29
  @sector,
30
30
  @size].flatten
31
- puts data.inspect
32
31
  data.pack(PACKING)
33
32
  end
34
33
 
@@ -119,7 +119,7 @@ module Axlsx
119
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
120
  # @param [Any] v The value validated
121
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
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, HYPERLINK_R], v
123
123
  end
124
124
 
125
125
  # Requires that the value is a valid table element type
@@ -1,7 +1,9 @@
1
1
  module Axlsx
2
- # version
2
+
3
+ # The version of the gem
3
4
  # When using bunle exec rake and referencing the gem on github or locally
4
5
  # it will use the gemspec, which preloads this constant for the gem's version.
5
6
  # We check to make sure that it has not already been loaded
6
- VERSION="1.0.15" unless Axlsx.const_defined? :VERSION
7
+ VERSION="1.0.16" unless Axlsx.const_defined? :VERSION
8
+
7
9
  end
@@ -82,17 +82,27 @@ require 'axlsx/workbook/worksheet/worksheet.rb'
82
82
  #end
83
83
 
84
84
  # Creates a new Workbook
85
- # @option options [Boolean] date1904
85
+ #
86
+ # @option options [Boolean] date1904. If this is not specified, we try to determine if the platform is bsd/darwin and set date1904 to true automatically.
87
+ #
86
88
  def initialize(options={})
87
89
  @styles = Styles.new
88
90
  @worksheets = SimpleTypedList.new Worksheet
89
91
  @drawings = SimpleTypedList.new Drawing
90
92
  @charts = SimpleTypedList.new Chart
91
93
  @images = SimpleTypedList.new Pic
92
- self.date1904= options[:date1904] unless options[:date1904].nil?
94
+ self.date1904= options[:date1904].nil? ? is_bsd? : options[:date1904]
93
95
  yield self if block_given?
94
96
  end
95
97
 
98
+ # Uses RUBY_PLATFORM constant to determine if the OS is freebsd or darwin
99
+ # based on this value we attempt to set date1904.
100
+ # @return [Boolean]
101
+ def is_bsd?
102
+ platform = RUBY_PLATFORM.downcase
103
+ platform.include?('freebsd') || platform.include?('darwin')
104
+ end
105
+
96
106
  # Instance level access to the class variable 1904
97
107
  # @return [Boolean]
98
108
  def date1904() @@date1904; end
@@ -144,7 +154,7 @@ require 'axlsx/workbook/worksheet/worksheet.rb'
144
154
  # @return [String]
145
155
  def to_xml()
146
156
  add_worksheet unless worksheets.size > 0
147
- builder = Nokogiri::XML::Builder.new(:encoding => ENCODING) do |xml|
157
+ builder = Nokogiri::XML::Builder.new(:encoding => ENCODING) do |xml|
148
158
  xml.workbook(:xmlns => XML_NS, :'xmlns:r' => XML_NS_R) {
149
159
  xml.workbookPr(:date1904=>@@date1904)
150
160
  #<x:workbookProtection workbookPassword="xsd:hexBinary data" lockStructure="1" lockWindows="1" />
@@ -155,7 +165,7 @@ require 'axlsx/workbook/worksheet/worksheet.rb'
155
165
  }
156
166
  }
157
167
  end
158
- builder.to_xml(:indent=>0)
168
+ builder.to_xml(:save_with => 0)
159
169
  end
160
170
  end
161
171
  end