diff --git a/lib/roo/excelx.rb b/lib/roo/excelx.rb index 91ebc1e0..ee4cbe40 100755 --- a/lib/roo/excelx.rb +++ b/lib/roo/excelx.rb @@ -25,8 +25,9 @@ class Excelx < Roo::Base require 'roo/excelx/coordinate' require 'roo/excelx/format' require 'roo/excelx/images' + require 'roo/excelx/drawing' - 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, :drawing_files] => :@shared ExceedsMaxError = Class.new(StandardError) # initialization and opening of a spreadsheet file @@ -460,6 +461,9 @@ 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$/ + nr = Regexp.last_match[1].to_i + drawing_files[nr - 1] = "#{@tmpdir}/roo_drawing#{nr}.xml" end entry.extract(path) if path diff --git a/lib/roo/excelx/drawing.rb b/lib/roo/excelx/drawing.rb new file mode 100644 index 00000000..65584ddc --- /dev/null +++ b/lib/roo/excelx/drawing.rb @@ -0,0 +1,58 @@ +require 'roo/excelx/extractor' + +module Roo + class Excelx + class Drawing < Excelx::Extractor + + # Returns: Hash { id1: [cell_coordinates] }, + # Example: { "rId1"=> [{ from_col: 2, from_row: 3, to_col: 2, to_row: 3 }, { from_col: 6, from_row: 3, to_col: 7, to_row: 3 }], + # "rId2"=> [{ from_col: 2, from_row: 4, to_col: 2, to_row: 4 }], + # "rId3"=> [{ from_col: 2, from_row: 5, to_col: 2, to_row: 5 }] } + # + def list + @image_coordinates ||= extract_image_coordinates + end + + private + + def extract_image_coordinates + return {} unless doc_exists? + data = Hash.new { |hash, key| hash[key] = [] } + + # Loop through all twoCellAnchor elements and extract the information + doc.xpath('//twoCellAnchor').each do |anchor| + # Extract the row and column numbers + from_col = anchor.at_xpath('./from/col').text.to_i + from_row = anchor.at_xpath('./from/row').text.to_i + to_col = anchor.at_xpath('./to/col').text.to_i + to_row = anchor.at_xpath('./to/row').text.to_i + + # Extract the rId attribute from the blip element if present, if not ignore anchor element + if anchor.at_xpath('./pic/blipFill/blip') + r_id = anchor.at_xpath('./pic/blipFill/blip')['embed'] + + # Store the extracted information in the data hash + data[r_id] << { from_col: from_col, from_row: from_row, to_col: to_col, to_row: to_row } + end + end + + # Loop through all oneCellAnchor elements and extract the information + doc.xpath('//oneCellAnchor').each do |anchor| + # Extract the row and column numbers + from_col = anchor.at_xpath('./from/col')&.text&.to_i + from_row = anchor.at_xpath('./from/row')&.text&.to_i + + # Extract the rId attribute from the blip element if present, if not ignore anchor element + if anchor.at_xpath('./pic/blipFill/blip') + r_id = anchor.at_xpath('./pic/blipFill/blip')['embed'] + + # Store the extracted information in the data hash + data[r_id] << { from_col: from_col, from_row: from_row } + end + end + + data + end + end + end +end diff --git a/lib/roo/excelx/shared.rb b/lib/roo/excelx/shared.rb index bcd2c08b..1af806a8 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, :drawing_files def initialize(dir, options = {}) @dir = dir @comments_files = [] @@ -13,6 +13,7 @@ def initialize(dir, options = {}) @options = options @image_rels = [] @image_files = [] + @drawing_files = [] end def styles diff --git a/lib/roo/excelx/sheet.rb b/lib/roo/excelx/sheet.rb index 840a0533..990e3c62 100644 --- a/lib/roo/excelx/sheet.rb +++ b/lib/roo/excelx/sheet.rb @@ -4,9 +4,9 @@ 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, :drawing_files, :image_files] => :@shared - attr_reader :images + attr_reader :images, :drawing def initialize(name, shared, sheet_index, options = {}) @name = name @@ -15,6 +15,7 @@ def initialize(name, shared, sheet_index, options = {}) @images = Images.new(image_rels[sheet_index]).list @rels = Relationships.new(rels_files[sheet_index]) @comments = Comments.new(comments_files[sheet_index]) + @drawing = Drawing.new(drawing_files[sheet_index]).list @sheet = SheetDoc.new(sheet_files[sheet_index], @rels, shared, options) end