lib/write_xlsx/workbook.rb in write_xlsx-1.11.2 vs lib/write_xlsx/workbook.rb in write_xlsx-1.12.0
- old
+ new
@@ -33,10 +33,13 @@
attr_reader :vba_project # :nodoc:
attr_reader :excel2003_style # :nodoc:
attr_reader :max_url_length # :nodoc:
attr_reader :strings_to_urls # :nodoc:
attr_reader :read_only # :nodoc:
+ attr_reader :embedded_image_indexes # :nodec:
+ attr_reader :embedded_images # :nodoc:
+ attr_reader :embedded_descriptions # :nodoc:
def initialize(file, *option_params)
options, default_formats = process_workbook_options(*option_params)
@writer = Package::XMLWriterSimple.new
@@ -78,18 +81,25 @@
@max_url_length = 2079
@has_comments = false
@read_only = 0
@has_metadata = false
+ @has_embedded_images = false
+ @has_embedded_descriptions = false
+
if options[:max_url_length]
@max_url_length = options[:max_url_length]
@max_url_length = 2079 if @max_url_length < 250
end
# Structures for the shared strings data.
@shared_strings = Package::SharedStrings.new
+ # Structures for embedded images.
+ @embedded_image_indexes = {}
+ @embedded_images = []
+
# Formula calculation default settings.
@calc_id = 124519
@calc_mode = 'auto'
@calc_on_load = true
@@ -528,10 +538,14 @@
def date_1904? # :nodoc:
@date_1904 ||= false
!!@date_1904
end
+ def has_dynamic_functions?
+ @has_dynamic_functions
+ end
+
#
# Add a string to the shared string table, if it isn't already there, and
# return the string index.
#
EMPTY_HASH = {}.freeze
@@ -595,10 +609,18 @@
def has_metadata?
@has_metadata
end
+ def has_embedded_images?
+ @has_embedded_images
+ end
+
+ def has_embedded_descriptions?
+ @has_embedded_descriptions
+ end
+
private
def filename
setup_filename unless @filename
@filename
@@ -1224,14 +1246,15 @@
#
# Set the metadata rel link.
#
def prepare_metadata
@worksheets.each do |sheet|
- if sheet.has_dynamic_arrays?
- @has_metadata = true
- break
- end
+ next unless sheet.has_dynamic_functions? || sheet.has_embedded_images?
+
+ @has_metadata = true
+ @has_dynamic_functions ||= sheet.has_dynamic_functions?
+ @has_embedded_images ||= sheet.has_embedded_images?
end
end
#
# Add "cached" data to charts to provide the numCache and strCache data for
@@ -1378,16 +1401,26 @@
#
# Iterate through the worksheets and set up any chart or image drawings.
#
def prepare_drawings # :nodoc:
chart_ref_id = 0
- image_ref_id = 0
drawing_id = 0
ref_id = 0
image_ids = {}
header_image_ids = {}
background_ids = {}
+
+ # Store the image types for any embedded images.
+ @embedded_images.each do |image_data|
+ store_image_types(image_data[1])
+
+ @has_embedded_descriptions = true if ptrue?(image_data[2])
+ end
+
+ # The image IDs start from after the embedded images.
+ image_ref_id = @embedded_images.size
+
@worksheets.each do |sheet|
chart_count = sheet.charts.size
image_count = sheet.images.size
shape_count = sheet.shapes.size
header_image_count = sheet.header_images.size
@@ -1506,175 +1539,22 @@
@drawing_count = drawing_id
end
#
- # Extract information from the image file such as dimension, type, filename,
- # and extension. Also keep track of previously seen images to optimise out
- # any duplicates.
+ # Store the image types (PNG/JPEG/etc) used in the workbook to use in these
+ # Content_Types file.
#
- def get_image_properties(filename)
- # Note the image_id, and previous_images mechanism isn't currently used.
- x_dpi = 96
- y_dpi = 96
-
- # Open the image file and import the data.
- data = File.binread(filename)
- md5 = Digest::MD5.hexdigest(data)
- if data.unpack1('x A3') == 'PNG'
- # Test for PNGs.
- type, width, height, x_dpi, y_dpi = process_png(data)
+ def store_image_types(type)
+ case type
+ when 'png'
@image_types[:png] = 1
- elsif data.unpack1('n') == 0xFFD8
- # Test for JPEG files.
- type, width, height, x_dpi, y_dpi = process_jpg(data, filename)
+ when 'jpeg'
@image_types[:jpeg] = 1
- elsif data.unpack1('A4') == 'GIF8'
- # Test for GIFs.
- type, width, height, x_dpi, y_dpi = process_gif(data, filename)
+ when 'gif'
@image_types[:gif] = 1
- elsif data.unpack1('A2') == 'BM'
- # Test for BMPs.
- type, width, height = process_bmp(data, filename)
+ when 'bmp'
@image_types[:bmp] = 1
- else
- # TODO. Add Image::Size to support other types.
- raise "Unsupported image format for file: #{filename}\n"
end
-
- # Set a default dpi for images with 0 dpi.
- x_dpi = 96 if x_dpi == 0
- y_dpi = 96 if y_dpi == 0
-
- [type, width, height, File.basename(filename), x_dpi, y_dpi, md5]
- end
-
- #
- # Extract width and height information from a PNG file.
- #
- def process_png(data)
- type = 'png'
- width = 0
- height = 0
- x_dpi = 96
- y_dpi = 96
-
- offset = 8
- data_length = data.size
-
- # Search through the image data to read the height and width in th the
- # IHDR element. Also read the DPI in the pHYs element.
- while offset < data_length
-
- length = data[offset + 0, 4].unpack1("N")
- png_type = data[offset + 4, 4].unpack1("A4")
-
- case png_type
- when "IHDR"
- width = data[offset + 8, 4].unpack1("N")
- height = data[offset + 12, 4].unpack1("N")
- when "pHYs"
- x_ppu = data[offset + 8, 4].unpack1("N")
- y_ppu = data[offset + 12, 4].unpack1("N")
- units = data[offset + 16, 1].unpack1("C")
-
- if units == 1
- x_dpi = x_ppu * 0.0254
- y_dpi = y_ppu * 0.0254
- end
- end
-
- offset = offset + length + 12
-
- break if png_type == "IEND"
- end
- raise "#{filename}: no size data found in png image.\n" unless height
-
- [type, width, height, x_dpi, y_dpi]
- end
-
- def process_jpg(data, filename)
- type = 'jpeg'
- x_dpi = 96
- y_dpi = 96
-
- offset = 2
- data_length = data.bytesize
-
- # Search through the image data to read the JPEG markers.
- while offset < data_length
- marker = data[offset + 0, 2].unpack1("n")
- length = data[offset + 2, 2].unpack1("n")
-
- # Read the height and width in the 0xFFCn elements
- # (Except C4, C8 and CC which aren't SOF markers).
- if (marker & 0xFFF0) == 0xFFC0 &&
- marker != 0xFFC4 && marker != 0xFFCC
- height = data[offset + 5, 2].unpack1("n")
- width = data[offset + 7, 2].unpack1("n")
- end
-
- # Read the DPI in the 0xFFE0 element.
- if marker == 0xFFE0
- units = data[offset + 11, 1].unpack1("C")
- x_density = data[offset + 12, 2].unpack1("n")
- y_density = data[offset + 14, 2].unpack1("n")
-
- if units == 1
- x_dpi = x_density
- y_dpi = y_density
- elsif units == 2
- x_dpi = x_density * 2.54
- y_dpi = y_density * 2.54
- end
- end
-
- offset += length + 2
- break if marker == 0xFFDA
- end
-
- raise "#{filename}: no size data found in jpeg image.\n" unless height
-
- [type, width, height, x_dpi, y_dpi]
- end
-
- #
- # Extract width and height information from a GIF file.
- #
- def process_gif(data, filename)
- type = 'gif'
- x_dpi = 96
- y_dpi = 96
-
- width = data[6, 2].unpack1("v")
- height = data[8, 2].unpack1("v")
-
- raise "#{filename}: no size data found in gif image.\n" if height.nil?
-
- [type, width, height, x_dpi, y_dpi]
- end
-
- # Extract width and height information from a BMP file.
- def process_bmp(data, filename) # :nodoc:
- type = 'bmp'
-
- # Check that the file is big enough to be a bitmap.
- raise "#{filename} doesn't contain enough data." if data.bytesize <= 0x36
-
- # Read the bitmap width and height. Verify the sizes.
- width, height = data.unpack("x18 V2")
- raise "#{filename}: largest image width #{width} supported is 65k." if width > 0xFFFF
- raise "#{filename}: largest image height supported is 65k." if height > 0xFFFF
-
- # Read the bitmap planes and bpp data. Verify them.
- planes, bitcount = data.unpack("x26 v2")
- raise "#{filename} isn't a 24bit true color bitmap." unless bitcount == 24
- raise "#{filename}: only 1 plane supported in bitmap image." unless planes == 1
-
- # Read the bitmap compression. Verify compression.
- compression = data.unpack1("x30 V")
- raise "#{filename}: compression not supported in bitmap image." unless compression == 0
-
- [type, width, height]
end
end
end