"""Чтение XLSX через openpyxl (полное чтение: нужны merged, outline level и шрифт).""" import io from typing import Any import openpyxl from src.parser.grid import Cell, MergedRange, Sheet from src.parser.readers._common import make_cell __all__ = ["read_xlsx"] def read_xlsx(data: bytes) -> tuple[Sheet, ...]: book = openpyxl.load_workbook(io.BytesIO(data), data_only=True, read_only=False) try: return tuple(_sheet(worksheet) for worksheet in book.worksheets) finally: book.close() def _sheet(ws: Any) -> Sheet: cells: dict[tuple[int, int], Cell] = {} # `_cells` — разреженное хранилище openpyxl (версия закреплена): iter_rows создал бы пустые ячейки на весь # прямоугольник листа for source in ws._cells.values(): cell = make_cell(source.value, bold=bool(source.font.bold), indent=int(source.alignment.indent or 0)) if cell is not None: cells[(source.row, source.column)] = cell merged = tuple( MergedRange(top=span.min_row, left=span.min_col, bottom=span.max_row, right=span.max_col) for span in ws.merged_cells.ranges ) row_outline = { row: int(dimension.outline_level) for row, dimension in ws.row_dimensions.items() if dimension.outline_level } return Sheet( name=str(ws.title), cells=cells, merged=merged, row_outline=row_outline, n_rows=int(ws.max_row), n_cols=int(ws.max_column), )