lib/write_xlsx/workbook.rb in write_xlsx-0.78.0 vs lib/write_xlsx/workbook.rb in write_xlsx-0.79.0
- old
+ new
@@ -983,11 +983,11 @@
@dxf_formats
]
end
def num_vml_files
- @worksheets.select { |sheet| sheet.has_vml? }.count
+ @worksheets.select { |sheet| sheet.has_vml? || sheet.has_header_vml? }.count
end
def num_comment_files
@worksheets.select { |sheet| sheet.has_comments? }.count
end
@@ -1532,22 +1532,36 @@
#
def prepare_vml_objects #:nodoc:
comment_id = 0
vml_drawing_id = 0
vml_data_id = 1
+ vml_header_id = 0
vml_shape_id = 1024
+ comment_files = 0
- @worksheets.select { |sheet| sheet.has_vml? }.each do |sheet|
- comment_id += 1 if sheet.has_comments?
- vml_drawing_id += 1
+ @worksheets.each do |sheet|
+ next if !sheet.has_vml? && !sheet.has_header_vml?
+ if sheet.has_vml?
+ if sheet.has_comments?
+ comment_files += 1
+ comment_id += 1
+ end
+ vml_drawing_id += 1
- sheet.prepare_vml_objects(vml_data_id, vml_shape_id,
- vml_drawing_id, comment_id)
+ sheet.prepare_vml_objects(vml_data_id, vml_shape_id,
+ vml_drawing_id, comment_id)
- # Each VML file should start with a shape id incremented by 1024.
- vml_data_id += 1 * ( 1 + sheet.num_comments_block )
- vml_shape_id += 1024 * ( 1 + sheet.num_comments_block )
+ # Each VML file should start with a shape id incremented by 1024.
+ vml_data_id += 1 * ( 1 + sheet.num_comments_block )
+ vml_shape_id += 1024 * ( 1 + sheet.num_comments_block )
+ end
+
+ if sheet.has_header_vml?
+ vml_header_id += 1
+ vml_drawing_id += 1
+ sheet.prepare_header_vml_objects(vml_header_id, vml_drawing_id)
+ end
end
add_font_format_for_cell_comments if num_comment_files > 0
end
@@ -1728,31 +1742,73 @@
drawing_id = 0
@worksheets.each do |sheet|
chart_count = sheet.charts.size
image_count = sheet.images.size
shape_count = sheet.shapes.size
- next if chart_count + image_count + shape_count == 0
+ header_image_count = sheet.header_images.size
+ footer_image_count = sheet.footer_images.size
+ has_drawing = false
- drawing_id += 1
+ # Check that some image or drawing needs to be processed.
+ next if chart_count + image_count + shape_count + header_image_count + footer_image_count == 0
+ # Don't increase the drawing_id header/footer images.
+ if chart_count + image_count + shape_count > 0
+ drawing_id += 1
+ has_drawing = true
+ end
+
+ # Prepare the worksheet charts.
sheet.charts.each_with_index do |chart, index|
chart_ref_id += 1
sheet.prepare_chart(index, chart_ref_id, drawing_id)
end
+ # Prepare the worksheet images.
sheet.images.each_with_index do |image, index|
- image_id, type, width, height, name = get_image_properties(image[2])
+ type, width, height, name, x_dpi, y_dpi = get_image_properties(image[2])
image_ref_id += 1
- sheet.prepare_image(index, image_ref_id, drawing_id, width, height, name, type)
+ sheet.prepare_image(index, image_ref_id, drawing_id, width, height, name, type, x_dpi, y_dpi)
end
+ # Prepare the worksheet shapes.
sheet.shapes.each_with_index do |shape, index|
sheet.prepare_shape(index, drawing_id)
end
- drawing = sheet.drawing
- @drawings << drawing
+ # Prepare the header images.
+ header_image_count.times do |index|
+ filename = sheet.header_images[index][0]
+ position = sheet.header_images[index][1]
+
+ type, width, height, name, x_dpi, y_dpi =
+ get_image_properties(filename)
+
+ image_ref_id += 1
+
+ sheet.prepare_header_image(image_ref_id, width, height,
+ name, type, position, x_dpi, y_dpi)
+ end
+
+ # Prepare the footer images.
+ footer_image_count.times do |index|
+ filename = sheet.footer_images[index][0]
+ position = sheet.footer_images[index][1]
+
+ type, width, height, name, x_dpi, y_dpi =
+ get_image_properties(filename)
+
+ image_ref_id += 1
+
+ sheet.prepare_header_image(image_ref_id, width, height,
+ name, type, position, x_dpi, y_dpi)
+ end
+
+ if has_drawing
+ drawing = sheet.drawing
+ @drawings << drawing
+ end
end
# Sort the workbook charts references into the order that the were
# written from the worksheets above.
@charts = @charts.sort_by { |chart| chart.id }
@@ -1764,80 +1820,119 @@
# 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.
#
def get_image_properties(filename)
- previous_images = []
- images_seen = {}
- image_id = 1;
- if images_seen[filename]
- # We've processed this file already.
- index = images_seen[filename] - 1
- else
- # Open the image file and import the data.
- data = File.binread(filename)
- if data.unpack('x A3')[0] == 'PNG'
- # Test for PNGs.
- type, width, height = process_png(data)
- @image_types[:png] = 1
- elsif data.unpack('n')[0] == 0xFFD8
- # Test for JPEG files.
- type, width, height = process_jpg(data, filename)
- @image_types[:jpeg] = 1
- elsif data.unpack('A2')[0] == 'BM'
- # Test for BMPs.
- type, width, height = process_bmp(data, filename)
- @image_types[:bmp] = 1
- else
- # TODO. Add Image::Size to support other types.
- raise "Unsupported image format for file: #{filename}\n"
- end
+ # Note the image_id, and previous_images mechanism isn't currently used.
+ x_dpi = 96
+ y_dpi = 96
- @images << [filename, type]
-
- # Also store new data for use in duplicate images.
- previous_images << [image_id, type, width, height]
- images_seen[filename] = image_id
- image_id += 1
+ # Open the image file and import the data.
+ data = File.binread(filename)
+ if data.unpack('x A3')[0] == 'PNG'
+ # Test for PNGs.
+ type, width, height, x_dpi, y_dpi = process_png(data)
+ @image_types[:png] = 1
+ elsif data.unpack('n')[0] == 0xFFD8
+ # Test for JPEG files.
+ type, width, height, x_dpi, y_dpi = process_jpg(data, filename)
+ @image_types[:jpeg] = 1
+ elsif data.unpack('A2')[0] == 'BM'
+ # Test for BMPs.
+ type, width, height = process_bmp(data, filename)
+ @image_types[:bmp] = 1
+ else
+ # TODO. Add Image::Size to support other types.
+ raise "Unsupported image format for file: #{filename}\n"
end
- [image_id, type, width, height, File.basename(filename)]
+ @images << [filename, type]
+
+ [type, width, height, File.basename(filename), x_dpi, y_dpi]
end
#
# Extract width and height information from a PNG file.
#
def process_png(data)
type = 'png'
- width = data[16, 4].unpack("N")[0]
- height = data[20, 4].unpack("N")[0]
+ width = 0
+ height = 0
+ x_dip = 96
+ y_dpi = 96
- [type, width, height]
+ 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].unpack("N")[0]
+ png_type = data[offset + 4, 4].unpack("A4")[0]
+
+ case png_type
+ when "IHDR"
+ width = data[offset + 8, 4].unpack("N")[0]
+ height = data[offset + 12, 4].unpack("N")[0]
+ when "pHYs"
+ x_ppu = data[offset + 8, 4].unpack("N")[0]
+ y_ppu = data[offset + 12, 4].unpack("N")[0]
+ units = data[offset + 16, 1].unpack("C")[0]
+
+ 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 find the 0xFFC0 marker. The height and
- # width are contained in the data for that sub element.
+ # Search through the image data to read the height and width in the
+ # 0xFFC0/C2 element. Also read the DPI in the 0xFFE0 element.
while offset < data_length
- marker = data[offset, 2].unpack("n")[0]
+ marker = data[offset+0, 2].unpack("n")[0]
length = data[offset+2, 2].unpack("n")[0]
if marker == 0xFFC0 || marker == 0xFFC2
height = data[offset+5, 2].unpack("n")[0]
width = data[offset+7, 2].unpack("n")[0]
- break
end
+ if marker == 0xFFE0
+ units = data[offset + 11, 1].unpack("C")[0]
+ x_density = data[offset + 12, 2].unpack("n")[0]
+ y_density = data[offset + 14, 2].unpack("n")[0]
+ 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]
+ [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'