xx2html converts Excel workbooks (.xlsx) into a single HTML file while preserving:
- Cell formatting and styles
- Conditional formatting classes (via
condif2css) - Worksheet link behavior
- Embedded worksheet images and in-cell rich-value images
Repository: https://github.com/gocova/xx2html
Issues: https://github.com/gocova/xx2html/issues
pip install xx2htmlfrom xx2html import apply_openpyxl_patches, create_xlsx_transform
# Explicit entrypoint. Patches are also applied automatically on import.
apply_openpyxl_patches()
transform = create_xlsx_transform(
sheet_html=(
'<section id="{enc_sheet_name}" data-sheet-name="{sheet_name}">'
"{table_generated_html}"
"</section>"
),
sheetname_html='<a class="sheet-nav" href="#{enc_sheet_name}">{sheet_name}</a>',
index_html=(
"<!doctype html><html><head>"
"{fonts_html}{core_css_html}{user_css_html}{generated_css_html}"
"{generated_incell_css_html}{conditional_css_html}"
'</head><body data-source="{source_filename}">{sheets_names_generated_html}{sheets_generated_html}'
"{safari_js}</body></html>"
),
fonts_html="",
core_css="",
user_css="",
safari_js="",
apply_cf=True,
max_sheets=3, # optional preview limit
max_rows=200, # optional preview limit per sheet
max_cols=20, # optional preview limit per sheet
raise_on_error=False,
)
ok, err = transform("input.xlsx", "output.html", "en_US")
if not ok:
raise RuntimeError(err)Public API (xx2html):
apply_openpyxl_patches() -> None- Applies required openpyxl monkey patches (idempotent).
create_xlsx_transform(...) -> Callable[[str, str, str], tuple[bool, str | None]]- Returns a transformer callable with signature
(source_xlsx, dest_html, locale). - Returns
(True, None)on success,(False, "<error repr>")on failure. - Optional preview controls:
max_sheets: convert only the first N visible sheets.max_rows: convert only the first N rows per included sheet.max_cols: convert only the first N columns per included sheet.
- Optional error mode:
raise_on_error=Trueraises the original exception instead of returning(False, ...).
- Returns a transformer callable with signature
Core helpers (xx2html.core, useful for advanced integrations):
get_worksheet_contents(...) -> WorksheetContentscova_render_table(worksheet_contents) -> strget_incell_images_refs(archive) -> tuple[dict[str, str], Exception | None]get_incell_css(...) -> strapply_cf_styles(html, cf_style_relations) -> strupdate_links(html, encoded_sheet_names, ...) -> str
sheet_html requires:
{enc_sheet_name}{sheet_name}{table_generated_html}
sheetname_html requires:
{enc_sheet_name}{sheet_name}
index_html requires:
{sheets_generated_html}{sheets_names_generated_html}{source_filename}{fonts_html}{core_css_html}{user_css_html}{generated_css_html}{generated_incell_css_html}{conditional_css_html}
index_html optional:
{safari_js}- If omitted while
safari_jsis non-empty, xx2html logs a warning and skips injection.
- If omitted while
Generated output also includes:
<meta name="generator" content="xx2html {version}">in<head><!-- Generated by xx2html {version} -->as the first node in<body>
xx2html relies on an openpyxl monkey patch to carry rich-value metadata used for in-cell images.
- The patch is applied automatically when
xx2html.coreis imported. - The explicit API entrypoint is
apply_openpyxl_patches(). xx2htmlvalidates theopenpyxlmajor/minor version before patching.- Set
XX2HTML_ALLOW_UNSUPPORTED_OPENPYXL=1to bypass the guard.
- Set
pdm sync --group dev --frozen-lockfile
python3 tests/scripts/generate_fixtures.py
pdm run python -m compileall src tests
ruff check src tests
mypy src/xx2html
pdm run pytest- Stable releases are tag-driven and use SemVer tags:
vMAJOR.MINOR.PATCH(for examplev1.2.3). - Push the tag to GitHub; the
publishworkflow builds from SCM metadata and publishes with PyPI Trusted Publishing.
xx2html is dual-licensed under MIT or Apache-2.0.