lib/axlsx/package.rb in axlsx-1.3.6 vs lib/axlsx/package.rb in axlsx-2.0.0
- old
+ new
@@ -15,16 +15,18 @@
# Initializes your package
#
# @param [Hash] options A hash that you can use to specify the author and workbook for this package.
# @option options [String] :author The author of the document
+ # @option options [Time] :created_at Timestamp in the document properties (defaults to current time).
# @option options [Boolean] :use_shared_strings This is passed to the workbook to specify that shared strings should be used when serializing the package.
# @example Package.new :author => 'you!', :workbook => Workbook.new
def initialize(options={})
@workbook = nil
@core, @app = Core.new, App.new
@core.creator = options[:author] || @core.creator
+ @core.created = options[:created_at]
parse_options options
yield self if block_given?
end
# Shortcut to specify that the workbook should use autowidth
@@ -33,23 +35,23 @@
Axlsx::validate_boolean(v);
workbook.use_autowidth = v
end
- # Shortcut to specify that the workbook should use shared strings
- # @see Workbook#use_shared_strings
- def use_shared_strings=(v)
- Axlsx::validate_boolean(v);
- workbook.use_shared_strings = v
- end
# Shortcut to determine if the workbook is configured to use shared strings
# @see Workbook#use_shared_strings
def use_shared_strings
workbook.use_shared_strings
end
+ # Shortcut to specify that the workbook should use shared strings
+ # @see Workbook#use_shared_strings
+ def use_shared_strings=(v)
+ Axlsx::validate_boolean(v);
+ workbook.use_shared_strings = v
+ end
# The workbook this package will serialize or validate.
# @return [Workbook] If no workbook instance has been assigned with this package a new Workbook instance is returned.
# @raise ArgumentError if workbook parameter is not a Workbook instance.
# @note As there are multiple ways to instantiate a workbook for the package,
# here are a few examples:
@@ -96,10 +98,11 @@
# # Serialize to a stream
# s = p.to_stream()
# File.open('example_streamed.xlsx', 'w') { |f| f.write(s.read) }
def serialize(output, confirm_valid=false)
return false unless !confirm_valid || self.validate.empty?
+ Relationship.clear_cached_instances
Zip::ZipOutputStream.open(output) do |zip|
write_parts(zip)
end
true
end
@@ -108,10 +111,11 @@
# Serialize your workbook to a StringIO instance
# @param [Boolean] confirm_valid Validate the package prior to serialization.
# @return [StringIO|Boolean] False if confirm_valid and validation errors exist. rewound string IO if not.
def to_stream(confirm_valid=false)
return false unless !confirm_valid || self.validate.empty?
+ Relationship.clear_cached_instances
zip = write_parts(Zip::ZipOutputStream.new("streamed", true))
stream = zip.close_buffer
stream.rewind
stream
end
@@ -154,23 +158,39 @@
# @return [Zip::ZipOutputStream]
def write_parts(zip)
p = parts
p.each do |part|
unless part[:doc].nil?
- zip.put_next_entry(part[:entry])
+ zip.put_next_entry(zip_entry_for_part(part))
entry = ['1.9.2', '1.9.3'].include?(RUBY_VERSION) ? part[:doc].force_encoding('BINARY') : part[:doc]
zip.puts(entry)
end
unless part[:path].nil?
- zip.put_next_entry(part[:entry]);
+ zip.put_next_entry(zip_entry_for_part(part))
# binread for 1.9.3
zip.write IO.respond_to?(:binread) ? IO.binread(part[:path]) : IO.read(part[:path])
end
end
zip
end
+ # Generate a ZipEntry for the given package part.
+ # The important part here is to explicitly set the timestamp for the zip entry: Serializing axlsx packages
+ # with identical contents should result in identical zip files – however, the timestamp of a zip entry
+ # defaults to the time of serialization and therefore the zip file contents would be different every time
+ # the package is serialized.
+ #
+ # Note: {Core#created} also defaults to the current time – so to generate identical axlsx packages you have
+ # to set this explicitly, too (eg. with `Package.new(created_at: Time.local(2013, 1, 1))`).
+ #
+ # @param part A hash describing a part of this pacakge (see {#parts})
+ # @return [Zip::ZipEntry]
+ def zip_entry_for_part(part)
+ timestamp = Zip::DOSTime.at(@core.created.to_i)
+ Zip::ZipEntry.new("", part[:entry], "", "", 0, 0, Zip::ZipEntry::DEFLATED, 0, timestamp)
+ end
+
# The parts of a package
# @return [Array] An array of hashes that define the entry, document and schema for each part of the package.
# @private
def parts
parts = [
@@ -319,12 +339,12 @@
# Creates the relationships required for a valid xlsx document
# @return [Relationships]
# @private
def relationships
rels = Axlsx::Relationships.new
- rels << Relationship.new(WORKBOOK_R, WORKBOOK_PN)
- rels << Relationship.new(CORE_R, CORE_PN)
- rels << Relationship.new(APP_R, APP_PN)
+ rels << Relationship.new(self, WORKBOOK_R, WORKBOOK_PN)
+ rels << Relationship.new(self, CORE_R, CORE_PN)
+ rels << Relationship.new(self, APP_R, APP_PN)
rels.lock
rels
end
end
end