Created
August 14, 2010 20:25
-
-
Save miau/524683 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
$KCODE = 'u' | |
# Excel の罫線確認用に HTML に吐き出す(Excel データ取得→YAML として吐き出す部分) | |
# 文字コード変換を簡単に行うためのヘルパメソッド | |
# | |
# 基本的に UTF-8 で統一したいが、ファイルシステムとのやりとりは Shift_JIS なので。 | |
# "あいう".sjis で Shift_JIS に、"あいう".utf8 で UTF-8 に変換する | |
require 'nkf' | |
class String | |
def sjis; NKF.nkf('-Ws', self); end | |
def utf8; NKF.nkf('-Sw', self); end | |
end | |
# 入出力ファイルの設定 | |
xls_file = "#{Dir.pwd.utf8}/xls2html_simple.xls" | |
yml_file = "#{Dir.pwd.utf8}/xls2html.yml" | |
require 'win32ole' | |
WIN32OLE.codepage = WIN32OLE::CP_UTF8 | |
# xlXXX 系の定数を使うためのおまじない。 | |
# xlXXX は Excel::XlXXX として利用できるようになる。(先頭は大文字になるので注意) | |
module Excel | |
end | |
WIN32OLE.const_load('Microsoft Excel 11.0 Object Library', Excel) | |
# Excel オブジェクトの準備 | |
excel = WIN32OLE.new('Excel.Application') | |
# Excel を表示していた場合、スクロールが発生すると動作が非常に重くなるのでので気を付けること | |
excel.visible = TRUE | |
book = excel.Workbooks.Open( | |
xls_file, | |
false, # UpdateLinks: 更新なし | |
true # ReadOnly: true | |
) | |
book.Activate | |
data = [] | |
1.upto(book.Worksheets.Count){|i| | |
sheet = book.Worksheets.Item(i) | |
puts "\t" + sheet.Name | |
sheet.Activate | |
# 末尾のセルを取得しておく | |
last_cell = excel.ActiveCell.SpecialCells(Excel::XlLastCell); | |
max_row, max_column = last_cell.Row, last_cell.Column | |
# 末尾のセルの右下を一時セルとして利用する | |
tmp_cell = sheet.Cells(max_row + 1, max_column + 1) | |
i = 0 | |
sheet.Range(sheet.Cells(1, 1), last_cell).Cells.each{|cell| | |
# 罫線の状態を素直に取得すると隣接セルの情報も拾ってしまうので、 | |
# 一旦右下の利用されていないセルにコピーする | |
cell.Select | |
excel.Selection.Copy | |
tmp_cell.Select | |
excel.ActiveSheet.Paste | |
data << [] if i % max_column == 0 | |
width = tmp_cell.MergeArea.Columns.Count | |
height = tmp_cell.MergeArea.Rows.Count | |
# 罫線については MergeArea から拾わないと、XlEdgeRight が異なることがあったので注意 | |
data.last << { | |
:value => tmp_cell.Value, | |
:width => width, | |
:height => height, | |
:left => tmp_cell.MergeArea.Borders(Excel::XlEdgeLeft ).LineStyle, | |
:top => tmp_cell.MergeArea.Borders(Excel::XlEdgeTop ).LineStyle, | |
:bottom => tmp_cell.MergeArea.Borders(Excel::XlEdgeBottom).LineStyle, | |
:right => tmp_cell.MergeArea.Borders(Excel::XlEdgeRight ).LineStyle, | |
:left_weight => tmp_cell.MergeArea.Borders(Excel::XlEdgeLeft ).Weight, | |
:top_weight => tmp_cell.MergeArea.Borders(Excel::XlEdgeTop ).Weight, | |
:bottom_weight => tmp_cell.MergeArea.Borders(Excel::XlEdgeBottom).Weight, | |
:right_weight => tmp_cell.MergeArea.Borders(Excel::XlEdgeRight ).Weight, | |
} | |
i += 1 | |
# マージされたセルがコピーされたままだと前の罫線が残る?ようなので | |
# 結合を解除しておく。 | |
if width > 1 || height > 1 | |
excel.Selection.UnMerge | |
end | |
} | |
} | |
book.close(0) # SaveChanges: false | |
# YAML に出力する | |
require 'yaml' | |
File.open(yml_file.sjis, 'wb'){|f| | |
f.write data.to_yaml | |
} | |
excel.visible = TRUE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
$KCODE = 'u' | |
# Excel の罫線確認用に HTML に吐き出す(YAML からデータを取り出して HTML を出力する部分) | |
# 文字コード変換を簡単に行うためのヘルパメソッド | |
# | |
# 基本的に UTF-8 で統一したいが、ファイルシステムとのやりとりは Shift_JIS なので。 | |
# "あいう".sjis で Shift_JIS に、"あいう".utf8 で UTF-8 に変換する | |
require 'nkf' | |
class String | |
def sjis; NKF.nkf('-Ws', self); end | |
def utf8; NKF.nkf('-Sw', self); end | |
end | |
# 入出力ファイルの設定 | |
yml_file = "#{Dir.pwd}/xls2html.yml" | |
html_file = "#{Dir.pwd}/xls2html.html" | |
require 'yaml' | |
require "cgi" | |
require 'win32ole' | |
WIN32OLE.codepage = WIN32OLE::CP_UTF8 | |
# xlXXX 系の定数を使うためのおまじない。 | |
# xlXXX は Excel::XlXXX として利用できるようになる。(先頭は大文字になるので注意) | |
module Excel | |
end | |
WIN32OLE.const_load('Microsoft Excel 11.0 Object Library', Excel) | |
# YAML ファイルの読み込み | |
yaml = File.open(yml_file.sjis, 'rb'){|f| | |
f.read | |
} | |
rows = YAML::load(yaml) | |
# Excel の罫線情報を CSS のどの class に割り当てるかのマッピング | |
$css_class = { | |
Excel::XlContinuous => { | |
Excel::XlHairline => "hairline", | |
Excel::XlThin => "continuous1", | |
Excel::XlMedium => "continuous2", | |
Excel::XlThick => "continuous3", | |
}, | |
Excel::XlDash => { | |
Excel::XlThin => "dash1", | |
Excel::XlMedium => "dash2", | |
}, | |
Excel::XlDashDot => { | |
Excel::XlThin => "dash_dot1", | |
Excel::XlMedium => "dash_dot2", | |
}, | |
Excel::XlDashDotDot => { | |
Excel::XlThin => "dash_dot_dot1", | |
Excel::XlMedium => "dash_dot_dot2", | |
}, | |
Excel::XlDot => "dot", | |
Excel::XlDouble => "double", | |
Excel::XlLineStyleNone => "none", | |
Excel::XlSlantDashDot => "slant_dash_dot", | |
} | |
# カラムの罫線情報から、適切な CSS のクラスを返す | |
def get_class(column) | |
%w(left top bottom right).map{|edge| | |
css_class = $css_class[column[edge.to_sym]] | |
if css_class.class == Hash | |
# LineStyle で決まらない場合は Weight 配列で格納されている | |
css_class = css_class[column["#{edge}_weight".to_sym]] | |
end | |
edge + "-" + css_class | |
}.join(" ") | |
end | |
# カラムの結合情報から、適切な colspan、rowspan の記述を返す | |
def get_spans(column) | |
spans = "" | |
spans += %Q( rowspan="#{column[:height]}") if column[:height] > 1 | |
spans += %Q( colspan="#{column[:width]}") if column[:width] > 1 | |
spans | |
end | |
#------------------- | |
# HTML の書き出し | |
#------------------- | |
f = File.open(html_file.sjis, 'wb') | |
# ヘッダの出力 | |
f.puts <<HEADER; | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | |
<style type="text/css"> | |
body { background-color: whitesmoke; } | |
td { background-color: white; } | |
HEADER | |
# CSS のメイン部分出力 | |
# left、top、bottom、right それぞれの class を定義しておく | |
# (HTML を単純化するために全体を出力する class も定義しているが、未使用) | |
["", "left-", "top-", "bottom-", "right-"].each{|edge| | |
f.puts <<-STYLES; | |
td.#{edge}none { border-#{edge}width: 1; border-#{edge}style: none; border-#{edge}color: black; } | |
td.#{edge}hairline { border-#{edge}width: 1; border-#{edge}style: solid; border-#{edge}color: gray; } | |
td.#{edge}dot { border-#{edge}width: 1; border-#{edge}style: dotted; border-#{edge}color: black; } | |
td.#{edge}dash_dot_dot1 { border-#{edge}width: 1; border-#{edge}style: groove; border-#{edge}color: black; } | |
td.#{edge}dash_dot1 { border-#{edge}width: 1; border-#{edge}style: ridge; border-#{edge}color: black; } | |
td.#{edge}dash1 { border-#{edge}width: 1; border-#{edge}style: dashed; border-#{edge}color: black; } | |
td.#{edge}continuous1 { border-#{edge}width: 1; border-#{edge}style: solid; border-#{edge}color: black; } | |
td.#{edge}dash_dot_dot2 { border-#{edge}width: 2; border-#{edge}style: groove; border-#{edge}color: black; } | |
td.#{edge}slant_dash_dot { border-#{edge}width: 2; border-#{edge}style: ridge; border-#{edge}color: black; } | |
td.#{edge}dash_dot2 { border-#{edge}width: 2; border-#{edge}style: dotted; border-#{edge}color: black; } | |
td.#{edge}dash2 { border-#{edge}width: 2; border-#{edge}style: dashed; border-#{edge}color: black; } | |
td.#{edge}continuous2 { border-#{edge}width: 2; border-#{edge}style: solid; border-#{edge}color: black; } | |
td.#{edge}continuous3 { border-#{edge}width: 3; border-#{edge}style: solid; border-#{edge}color: black; } | |
td.#{edge}double { border-#{edge}width: 3; border-#{edge}style: double; border-#{edge}color: black; } | |
STYLES | |
} | |
f.puts <<BODY_HEADER; | |
</style> | |
</head> | |
<body> | |
<table> | |
BODY_HEADER | |
# テーブルの出力 | |
merged_cells = {} # マージされたセルの座標を格納する二次元ハッシュ。merged_cells[row][column] = true | |
rows.each_with_index{|row, r| | |
f.puts "<tr>" | |
row.each_with_index{|column, c| | |
# マージされたセルであればスキップ | |
next if merged_cells.has_key?(r) && merged_cells[r].delete(c) | |
# css の class を取得 | |
css_class = get_class(column) | |
# colspan、rowspan を取得 | |
spans = get_spans(column) | |
if spans != "" | |
# マージされたセルを記録しておく | |
column[:height].times{|dy| | |
merged_cells[r + dy] = {} if !merged_cells.has_key?(r + dy) | |
column[:width].times{|dx| | |
merged_cells[r + dy][c + dx] = true | |
} | |
} | |
end | |
f.puts %(<td class="#{css_class}"#{spans}>) | |
if column[:value].nil? | |
# 空のセルは border が表示されるように を出力しておく | |
f.puts " " | |
else | |
f.puts CGI.escapeHTML(column[:value].to_s) | |
end | |
f.puts "</td>" | |
} | |
f.puts "</tr>" | |
} | |
f.puts <<FOOTER; | |
</table> | |
</body> | |
FOOTER |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment