Skip to content

[bug] Strikethrough-only cells are treated as unstyled #17

Description

@arnav2

Summary

Cells whose only non-default font attribute is strike=True lose their style entirely: cell.style comes back as None. The same happens for underline-only cells.

Reproduction

from pathlib import Path
from tempfile import TemporaryDirectory

import openpyxl
from openpyxl.styles import Font
from ks_xlsx_parser import parse_workbook

with TemporaryDirectory() as td:
    path = Path(td) / "strike_test.xlsx"
    wb = openpyxl.Workbook()
    ws = wb.active
    ws["A1"] = "Normal"
    ws["B1"] = "Strike"
    ws["B1"].font = Font(strike=True)
    ws["C1"] = "BoldStrike"
    ws["C1"].font = Font(bold=True, strike=True, color="FF0000")
    wb.save(path)

    sheet = parse_workbook(path=path).workbook.sheets[0]
    for col in range(1, 4):
        cell = sheet.get_cell(1, col)
        font = cell.style.font if cell.style and cell.style.font else None
        print(cell.coord.to_a1(), "has_style=", bool(cell.style), "font=",
              font.model_dump(exclude_none=True) if font else None)

Actual

A1 has_style= True  font= {'name': 'Calibri', 'size': 11.0, ..., 'strikethrough': False, 'color': 'theme:1'}
B1 has_style= False font= None
C1 has_style= True  font= {'bold': True, 'strikethrough': True, 'color': 'FF0000'}

Expected: B1 keeps its style and reports strikethrough: true.

C1 works only incidentally — bold/color are what keep the style alive there, not the strike.

Cause

_extract_font() in src/parsers/cell_parser.py parses strikethrough correctly, but _extract_style() decides whether a cell is styled from a hand-written attribute list that omits both strikethrough and underline:

has_style = any([
    font and (font.bold or font.italic or font.name or font.size or font.color),
    fill and fill.fg_color,
    border and any([border.left, border.right, border.top, border.bottom]),
    alignment and (alignment.horizontal or alignment.vertical or alignment.wrap_text),
    number_format,
])

When the whole CellStyle is discarded, the parsed font (strike included) goes with it.

The enumeration also under-reports two other cases: a fill with only bg_color, and an alignment with only text_rotation/indent. Each _extract_* helper already returns None when nothing is set, so the enumeration is redundant as well as lossy — comparing against a default DTO covers all of them:

has_style = any([
    font is not None and font != _DEFAULT_FONT,
    fill is not None,
    border is not None,
    alignment is not None,
    number_format,
])

Secondary: expose strikethrough in to_json()

Chunk cells in result.to_json()["chunks"][...]["cells"] currently carry address, value, formula, font_color, fill_color. Strikethrough is widely used in real workbooks to mark deprecated or void rows, so downstream RAG/LLM consumers need it at that level too, alongside the colors:

{"address": "B1", "value": "Strike", "formula": null,
 "font_color": null, "fill_color": null, "font_strikethrough": true}

Environment

Reported against ks-xlsx-parser 0.2.1, Python 3.13, Windows. Reproduced on main.

Reported by a user via project feedback.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions