@@ -107,6 +107,7 @@ def parse_to_json(
107107 sheet_name : str | None = None ,
108108 cell_range : str | None = None ,
109109 include_frozen_rows : bool = True ,
110+ include_cell_styles : bool = False ,
110111 ) -> str :
111112 """
112113 Excelファイルを解析してJSON形式で返す
@@ -118,13 +119,16 @@ def parse_to_json(
118119 include_frozen_rows: cell_range指定時に固定行(ヘッダー)を自動追加
119120 True(デフォルト): frozen_rowsで指定された行を自動的に取得
120121 False: 指定されたcell_rangeのみを取得
122+ include_cell_styles: セルの色・サイズ情報(default: false)
123+ 色分けデータ抽出時のみ使用。トークン消費+約20%
121124
122125 Returns:
123126 JSON文字列
124127 - 各セルのデータ: value(値)、coordinate(座標)
125128 - 構造情報: シート名、dimensions(シート全体のセル範囲、例: "A1:D10")
126129 - 構造情報: frozen_rows(固定行数)、frozen_cols(固定列数)
127130 - 条件付き構造情報: freeze_panes(存在する場合)、merged_ranges(結合セルが存在する場合)
131+ - スタイル情報(include_cell_styles=Trueの場合): fill(背景色)、width(列幅)、height(行高さ)
128132 """
129133 logger .info (
130134 f"Parsing Excel file: { file_path } (sheet={ sheet_name } , range={ cell_range } )"
@@ -205,6 +209,7 @@ def parse_to_json(
205209 sheet ,
206210 cell_range ,
207211 include_frozen_rows ,
212+ include_cell_styles ,
208213 )
209214 result ["sheets" ].append (sheet_data )
210215
@@ -289,9 +294,7 @@ def _scan_sheet(
289294 }
290295 )
291296
292- def _calculate_header_range (
293- self , cell_range : str , frozen_rows : int
294- ) -> str | None :
297+ def _calculate_header_range (self , cell_range : str , frozen_rows : int ) -> str | None :
295298 """
296299 セル範囲に対してfrozen_rowsに基づくヘッダー範囲を計算
297300
@@ -393,6 +396,7 @@ def _parse_sheet(
393396 sheet ,
394397 cell_range : str | None = None ,
395398 include_frozen_rows : bool = True ,
399+ include_cell_styles : bool = False ,
396400 ) -> dict [str , Any ]:
397401 """
398402 シートを解析してdict形式で返す
@@ -401,6 +405,7 @@ def _parse_sheet(
401405 sheet: openpyxl Worksheet
402406 cell_range: セル範囲指定(例: "A1:D10")
403407 include_frozen_rows: cell_range指定時に固定行(ヘッダー)を自動追加
408+ include_cell_styles: セルのスタイル情報を含めるか
404409
405410 Returns:
406411 シートデータのdict
@@ -467,7 +472,9 @@ def _parse_sheet(
467472
468473 # ヘッダー自動追加の場合、マージセルキャッシュにもヘッダー範囲を含める
469474 if include_frozen_rows and frozen_rows > 0 :
470- header_range = self ._calculate_header_range (effective_range , frozen_rows )
475+ header_range = self ._calculate_header_range (
476+ effective_range , frozen_rows
477+ )
471478 if header_range :
472479 # ヘッダー範囲とデータ範囲を結合した範囲を計算
473480 effective_range_for_merge = self ._merge_ranges (
@@ -519,6 +526,19 @@ def _parse_sheet(
519526 if merged_ranges :
520527 sheet_data ["merged_ranges" ] = merged_ranges
521528
529+ # セルサイズのキャッシュを構築(パフォーマンス最適化)
530+ col_widths : dict [str , float ] | None = None
531+ row_heights : dict [int , float ] | None = None
532+ if include_cell_styles :
533+ col_widths = {}
534+ row_heights = {}
535+ for col_letter , dim in sheet .column_dimensions .items ():
536+ if dim .width :
537+ col_widths [col_letter ] = dim .width
538+ for row_num , dim in sheet .row_dimensions .items ():
539+ if dim .height :
540+ row_heights [row_num ] = dim .height
541+
522542 # データ取得
523543 if cell_range :
524544 # ヘッダー自動追加(include_frozen_rows=Trueの場合)
@@ -530,8 +550,11 @@ def _parse_sheet(
530550 all_rows .extend (
531551 self ._parse_rows (
532552 header_rows ,
553+ include_cell_styles ,
533554 merged_cell_map ,
534555 merged_anchor_value_map ,
556+ col_widths ,
557+ row_heights ,
535558 )
536559 )
537560
@@ -541,8 +564,11 @@ def _parse_sheet(
541564 all_rows .extend (
542565 self ._parse_rows (
543566 rows_to_process ,
567+ include_cell_styles ,
544568 merged_cell_map ,
545569 merged_anchor_value_map ,
570+ col_widths ,
571+ row_heights ,
546572 )
547573 )
548574
@@ -554,8 +580,11 @@ def _parse_sheet(
554580 all_rows .extend (
555581 self ._parse_rows (
556582 rows_to_process ,
583+ include_cell_styles ,
557584 merged_cell_map ,
558585 merged_anchor_value_map ,
586+ col_widths ,
587+ row_heights ,
559588 )
560589 )
561590
@@ -741,16 +770,22 @@ def _expand_axis_range(self, range_str: str) -> str:
741770 def _parse_cell (
742771 self ,
743772 cell ,
773+ include_cell_styles : bool = False ,
744774 merged_cell_map : dict [str , str ] | None = None ,
745775 merged_anchor_value_map : dict [str , Any ] | None = None ,
776+ col_widths : dict [str , float ] | None = None ,
777+ row_heights : dict [int , float ] | None = None ,
746778 ) -> dict [str , Any ]:
747779 """
748780 セルを解析してdict形式で返す
749781
750782 Args:
751783 cell: openpyxl Cell
784+ include_cell_styles: セルのスタイル情報を含めるか(デフォルト: False)
752785 merged_cell_map: マージセル座標からマージ範囲へのマップ(パフォーマンス最適化用)
753786 merged_anchor_value_map: マージ範囲 -> アンカー値 のマップ(結合セルの値埋め用)
787+ col_widths: 列幅のキャッシュ(パフォーマンス最適化用)
788+ row_heights: 行高さのキャッシュ(パフォーマンス最適化用)
754789
755790 Returns:
756791 セルデータのdict
@@ -776,22 +811,53 @@ def _parse_cell(
776811 if anchor_value is not None :
777812 cell_data ["value" ] = anchor_value
778813
779- # 書式情報(fill/width/height/data_type)は現在サポートされていません
814+ # スタイル情報(include_cell_styles=Trueの場合のみ)
815+ if include_cell_styles :
816+ # 背景色情報
817+ if cell .fill and cell .fill .patternType :
818+ fill_info = {
819+ "pattern_type" : cell .fill .patternType ,
820+ }
821+ fg_color = self ._color_to_hex (cell .fill .fgColor )
822+ if fg_color :
823+ fill_info ["fg_color" ] = fg_color
824+ bg_color = self ._color_to_hex (cell .fill .bgColor )
825+ if bg_color :
826+ fill_info ["bg_color" ] = bg_color
827+ cell_data ["fill" ] = fill_info
828+
829+ # セルサイズ(列幅・行高さ)
830+ # MergedCellの場合は属性が存在しないため、hasattrでチェック
831+ if hasattr (cell , "column_letter" ) and hasattr (cell , "row" ):
832+ if cell .column_letter and cell .row :
833+ # キャッシュから列幅を取得(パフォーマンス最適化)
834+ if col_widths and cell .column_letter in col_widths :
835+ cell_data ["width" ] = col_widths [cell .column_letter ]
836+ # キャッシュから行高さを取得(パフォーマンス最適化)
837+ if row_heights and cell .row in row_heights :
838+ cell_data ["height" ] = row_heights [cell .row ]
839+
780840 return cell_data
781841
782842 def _parse_rows (
783843 self ,
784844 rows : tuple [tuple [Cell , ...], ...],
845+ include_cell_styles : bool = False ,
785846 merged_cell_map : dict [str , str ] | None = None ,
786847 merged_anchor_value_map : dict [str , Any ] | None = None ,
848+ col_widths : dict [str , float ] | None = None ,
849+ row_heights : dict [int , float ] | None = None ,
787850 ) -> list [list [dict [str , Any ]]]:
788851 """
789852 行データを解析してリスト形式で返す(コード重複削減用ヘルパー)
790853
791854 Args:
792855 rows: 行データのタプル
856+ include_cell_styles: セルのスタイル情報を含めるか
793857 merged_cell_map: マージセル情報
794858 merged_anchor_value_map: マージ範囲 -> アンカー値
859+ col_widths: 列幅のキャッシュ(パフォーマンス最適化用)
860+ row_heights: 行高さのキャッシュ(パフォーマンス最適化用)
795861
796862 Returns:
797863 解析された行データのリスト
@@ -801,8 +867,11 @@ def _parse_rows(
801867 row_data = [
802868 self ._parse_cell (
803869 cell ,
870+ include_cell_styles ,
804871 merged_cell_map ,
805872 merged_anchor_value_map ,
873+ col_widths ,
874+ row_heights ,
806875 )
807876 for cell in row
808877 ]
0 commit comments