吹き出しの情報を集める

Excelファイル内に散らばっている吹き出しに書いてある情報を集めてみる。 「吹き出し」というのは、こんなやつだ。 吹き出し

対象ファイル

いろんな吹き出しを含むExcelファイルを用意する。 テンプレート

このテンプレートを"auto_shapes.xls"という名前で保存しておく。

ステップ1:集めて表示

まずは、単純に集めて、属性を表示させてみよう。

吹き出しは、Excel上ではShapeクラスのオブジェクトになっている。つまり、図形の一種ということだ。 図形は必ずワークシート上に配置されるので、Worksheetオブジェクトに働きかけて取得できるようになっている。 Excelのオブジェクトブラウザを使って、Worksheetクラスのプロパティやメソッドを調べてみると、 Shapesプロパティというのが見つかる。

Worksheet#Shapes*1は ワークシート内のShapeオブジェクトのコレクションを返す。「コレクション」というのは、オブジェクトの配列だと思えばいい 。

WIN32OLEライブラリのおかげで、コレクションオブジェクトにはeachメソッドが定義されているので、コレクション内の要素を抜き出しながら処理できる。

では、ワークブック内のすべての吹き出しの属性を表示するスクリプトを以下に示す。

  # fukidashi.rb
  require 'excel_lib'
  
  excel = Excel.new
  book = excel.copy_book("./auto_shapes.xls")
  book.each_sheet {|sheet|
    sheet.shapes.each {|shape|
      p [shape.Type, shape.AutoShapeType, shape.textframe.characters.text]
    }
  }
  

結果は以下のようになる。

  ruby -Ks fukidashi.rb
  [2, 110, "四角/線無し"]
  [1, 106, "角丸吹き出し"]
  [1, 107, "丸型吹き出し"]
  [2, 122, "四角/横線付き"]
  [1, 108, "雲形吹き出し"]
  [1, 105, "四角吹き出し"]
  [2, 114, "横線付き、枠無し"]
  [2, 111, "四角/横線付き/途中で降り曲がり"]
  

shape.Typeは図形の大分類、shape.AutoShapeTypeは小分類というところだろうか。

実は、このスクリプトでは、線などが入ったExcelファイルに適用するとエラーになる。線にはTextFrameがないからだ。

ステップ2:吹き出しだけを集める

では、吹き出しだけを集めてみる。

Shape#TypeとShape#AutoShapeTypeで分類するのが王道のような気がするが、種類がたくさんあって大変だ。 ここは安易にshape.textframecharacters.textがテキストを返すかどうかで吹き出しかどうかを判定してみよう。 判定にはbegin/rescueを使えばいいだろう。

ただ、他の図形とグルーピングされている吹き出しはひっかからない。

  require 'excel_lib'
  
  def fukidashi?(shape)
    begin
      text = shape.textframe.characters.text
      return true
    rescue WIN32OLERuntimeError
      return nil
    end
  end
  
  excel = Excel.new
  book = excel.copy_book("./auto_shapes.xls")
  book.each_sheet {|sheet|
    sheet.shapes.each {|shape|
      if fukidashi?(shape)
        p [shape.Type, shape.AutoShapeType, shape.textframe.characters.text]
      end
    }
  }
  

グルーピングされている図形の中から吹き出しを得るには

やってみたらこれが以外と難しい。 まず、グルーピングされた図形は、Worksheet#shapesで得られた図形コレクション内の1つの要素として管理されている。 ある図形要素がグルーピングされているかどうかは、Shape#Typeの値で判断する。

定数でいうと、MsoGroup(17)だ。これらの定数は、Office共通の定数なので、残念ながらWIN32OLEを使っても定数としては得られない(ruby-list:30413からのスレッド参照)。

ということで、適当な吹き出しと線などをグルーピングしたExcelファイルを用意して、グルーピングした図形を処理してみよう。

まずは、グルーピングされた図形を見つけたら、shape.GroupItemsでグルーピング内の図形のコレクションを得て、各図形の文字を得てみよう。

  require 'excel_lib'
  
  Excel::MsoGroup = 6
  
  def fukidashi?(shape)
    begin
      text = shape.textframe.characters.text
      return true
    rescue WIN32OLERuntimeError
      return nil
    end
  end
  
  excel = Excel.new
  book = excel.copy_book("./auto_shapes.xls")
  book.each_sheet {|sheet|
    sheet.shapes.each {|shape|
      if shape.Type == Excel::MsoGroup
        group_shapes = shape.GroupItems
        group_shapes.each {|sub_shape|
          if fukidashi?(sub_shape)
            obj = sub_shape.textframe.characters
            p ["グループ", sub_shape.Type, sub_shape.AutoShapeType, sub_shape.textframe.characters.text]
          else
            p ["グループ", sub_shape.Type, sub_shape.AutoShapeType]
          end
        }
      end
    }
  }  
  

これでいいだろうとおもいきや、結果はこうなってしまった。

  ["グループ", 1, 108, -2146826246]
  ["グループ", 9, -2]
  

吹き出しと線をグルーピングした図形が引っかかかり、GroupItemsでちゃんと吹き出しと線が得られているんだが、なぜかShape#TextFrame#Characters#textが吹き出しに 設定された文字列を返してくれない。

しょうがないので、いったんグルーピングを解除してからアクセスしてみよう。 グルーピングされたShapeオブジェクトにUnGroupメソッドを作用させると、グルーピングが解除される。このメソッドの戻り値はShapeRangeオブジェクトとなっている。 これもShapeコレクションの一種なので、eachメソッドで子要素を取り出すことができる。 調べ終わったら、ShapeRange#Groupメソッドで再度グルーピングしておこう。

  require 'excel_lib'
  
  Excel::MsoGroup = 6
  
  def fukidashi?(shape)
    begin
      text = shape.textframe.characters.text
      return true
    rescue WIN32OLERuntimeError
      return nil
    end
  end
  
  excel = Excel.new
  book = excel.copy_book("./auto_shapes.xls")
  book.each_sheet {|sheet|
    sheet.shapes.each {|shape|
      if shape.Type == Excel::MsoGroup
        group_shapes = shape.UnGroup
        group_shapes.each {|sub_shape|
          if fukidashi?(sub_shape)
            obj = sub_shape.textframe.characters
            p ["グループ", sub_shape.Type, sub_shape.AutoShapeType, sub_shape.textframe.characters.text]
          else
            p ["グループ", sub_shape.Type, sub_shape.AutoShapeType]
          end
        }
        group_shapes.Group
      end
    }
  }
  

今度は、ちゃんと文字列を得ることができた。グルーピングを解除しなくても同じように振まってくれるとうれしいのだが。

  ["グループ", 1, 108, "雲形吹き出し"]
  ["グループ", 9, -2]
  


戻る


*1Rubyの流儀では、WorksheetクラスのShapesメソッド/プロパティをWorksheet#Shapesのように書く。