diff --git a/lib/roo/excelx.rb b/lib/roo/excelx.rb index 91ebc1e0..c45e8c63 100755 --- a/lib/roo/excelx.rb +++ b/lib/roo/excelx.rb @@ -26,7 +26,7 @@ class Excelx < Roo::Base require 'roo/excelx/format' require 'roo/excelx/images' - delegate [:styles, :workbook, :shared_strings, :rels_files, :sheet_files, :comments_files, :image_rels, :image_files] => :@shared + delegate [:styles, :workbook, :shared_strings, :rels_files, :sheet_files, :comments_files, :image_rels, :image_files, :drawings] => :@shared ExceedsMaxError = Class.new(StandardError) # initialization and opening of a spreadsheet file @@ -460,6 +460,12 @@ def process_zipfile_entries(entries) # Extracting drawing relationships to make images lists for each sheet nr = Regexp.last_match[1].to_i image_rels[nr - 1] = "#{@tmpdir}/roo_image_rels#{nr}" + when /drawing([0-9]+).xml$/ + # Extracting drawings to recover images for each sheet. + # This is conceptually wrong (we should be following rels from sheet{n}.xml.rels), + # but just happens to work for common cases. + nr = Regexp.last_match[1].to_i + drawings[nr - 1] = "#{@tmpdir}/roo_drawing#{nr}.xml" end entry.extract(path) if path diff --git a/lib/roo/excelx/shared.rb b/lib/roo/excelx/shared.rb index bcd2c08b..0e3fc6a8 100755 --- a/lib/roo/excelx/shared.rb +++ b/lib/roo/excelx/shared.rb @@ -4,7 +4,7 @@ class Excelx # reduce memory usage and reduce the number of objects being passed # to various inititializers. class Shared - attr_accessor :comments_files, :sheet_files, :rels_files, :image_rels, :image_files + attr_accessor :comments_files, :sheet_files, :rels_files, :image_rels, :image_files, :drawings def initialize(dir, options = {}) @dir = dir @comments_files = [] @@ -13,6 +13,7 @@ def initialize(dir, options = {}) @options = options @image_rels = [] @image_files = [] + @drawings = [] end def styles diff --git a/lib/roo/excelx/sheet.rb b/lib/roo/excelx/sheet.rb index 840a0533..395727e4 100644 --- a/lib/roo/excelx/sheet.rb +++ b/lib/roo/excelx/sheet.rb @@ -4,7 +4,7 @@ class Excelx class Sheet extend Forwardable - delegate [:styles, :workbook, :shared_strings, :rels_files, :sheet_files, :comments_files, :image_rels] => :@shared + delegate [:styles, :workbook, :shared_strings, :rels_files, :sheet_files, :comments_files, :image_rels, :drawings] => :@shared attr_reader :images @@ -14,6 +14,7 @@ def initialize(name, shared, sheet_index, options = {}) @sheet_index = sheet_index @images = Images.new(image_rels[sheet_index]).list @rels = Relationships.new(rels_files[sheet_index]) + @drawing_xml_path = drawings[sheet_index] @comments = Comments.new(comments_files[sheet_index]) @sheet = SheetDoc.new(sheet_files[sheet_index], @rels, shared, options) end @@ -96,6 +97,35 @@ def dimensions @sheet.dimensions end + def embedded_image_refs_anchored_to_single_cells + @embedded_image_refs_anchored_to_single_cells ||= begin + parsed_xml = Roo::Utils.load_xml(@drawing_xml_path).remove_namespaces! + nodes = parsed_xml.xpath('/wsDr/twoCellAnchor[pic/blipFill/blip[@embed]]') + + nodes.map do |node| + from = node.at_xpath('from') + from_coords = [from.at_xpath('row').content.to_i + 1, from.at_xpath('col').content.to_i + 1] + + to = node.at_xpath('to') + to_coords = [to.at_xpath('row').content.to_i + 1, to.at_xpath('col').content.to_i + 1] + + image_ref = node.at_xpath('pic/blipFill/blip')['embed'] + + [from_coords, to_coords, image_ref] + end.select do |from, to, image_ref| + (to[1] == from[1] + 1) && (to[0] == from[0] + 1) + end.map do |from, to, image_ref| + [from, image_ref] + end.inject(Hash.new) do |hsh, item| + from, image_ref = item + hsh[from] ||= Array.new + hsh[from] << image_ref + + hsh + end + end + end + private # Take an xml row and return an array of Excelx::Cell objects