Repository navigation
feat: cell styles on write (Core, C ABI 7 and every binding), and ExcelReader.NET.Framework (net472) - #169
Merged
Conversation
…italic styles.bin now writes one BrtFont, BrtFill and BrtBorder per deduplicated component and BrtXF carries font/fill/border ids, trot, indent, alignment and apply bits. Field layouts match an Excel-authored reference (COM SaveAs 50); BrtBorder order is top, bottom, left, right, diagonal. Unstyled output is byte-identical.
Cell XFs now carry their font, alignment, borders and solid fill; custom fonts follow the four defaults at index 5 and up (BIFF8 has no index 4). A PALETTE record holds the style colors first, then Excel's defaults, and is written only when a style uses a color. Unstyled globals are unchanged. Font size/flags and the BIFF vertical-alignment codes move to FontKey and StyleAlignment so XLSB and XLS share them. The BIFF record walker moves from XlsConformanceTests into StyleProbe.
ISheetWriter gains StartRow/StartRowAsync(styleId, height) as default members that throw NotSupportedException. XLSX writes ht/customHeight, XLSB and XLS write miyRw in twips with fUnsynced set. CSV validates and ignores the height.
…f-range read counts
… the native DLL through a once-only guard
… mapped properties
…ecimal precision and typed-read errors
…reach and xl_aggregate ExcelSheet.ForEach hands each row to a callback on the calling thread and ExcelSheet.Aggregate folds the data rows across worker threads through IExcelAccumulator<TSelf>. Rows arrive as ExcelRowView/ExcelCellView ref structs borrowing the native buffers, so nothing is copied per row; a callback exception is rethrown to the caller.
xl_sheet_options grows column_styles, column_widths and header_style (64 bytes) and still accepts the 40-byte ABI 6 layout. Column styles reach data cells only through the style overlay pushed after the header, keep the temporal number format, and survive parallel writes; styles are validated and registered before the sheet is added. xl_write_typed and xl_write_typed_to_memory are documented deprecated (removal in 8.0).
… style only written cells Temporal and user column styles are registered before the sheet is added and resolved through the workbook's InheritNumberFormat, replacing the native re-implementation; this also closes the window where a temporal style hitting a format limit poisoned the handle. A header-less sheet now pushes the same overlay once its first data row starts, so its cells, nulls included, are styled exactly as on the header path, serial and parallel. xl_sheet_options is read once per xl_write_sheet.
…, deprecation versions
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three additive features, all targeting 7.4.0:
Core's public API grows append-only. The C ABI moves to version 7 and stays additive: every ABI-6 function, struct and size still works.
1. Cell styles on write (Core: XLSX, XLSB, XLS)
CellStyleused to cover only number format and bold/italic, with bold/italic in XLSX only. It now has the full set below, written in all three formats.AddStylechecks the limits: 512 fonts; 65,490 cell styles in XLSX/XLSB and 4,000 in XLS. The 57th distinct color in XLS throwsExcelLimitExceededException.EXR011.[ExcelStyle(Bold = true)]on aTimeOnlyshowed0.5. An explicitNumberFormatstill wins, and an all-default style also keeps the column's format.2. ExcelReader.NET.Framework (net472)
A new NuGet package so .NET Framework 4.7.2+ apps can read and write XLSX, XLSB, XLS and CSV. It P/Invokes the existing NativeAOT
ExcelReader.Nativelibrary, the one the Python, Rust and C++ bindings use, instead of porting Core to netstandard.3. Cell styles in the C ABI and every binding (ABI 7)
xl_write_sheettakes per-column styles, a header style and column widths. Column styles reach data cells only, never the header, and only cells that are written.XL_INVALID_ARGUMENTand Core limits returnXL_ERROR. Neither poisons the handle.Bindings, each in its own idiom:
xl::CellStyle,xl::Border;WriterHandle::add_style / set_column_style / set_column_width / start_row(style, height) / style_next_cell;SheetStyleson typed writesCellStyle,Border(frozen dataclasses) and enums;write_sheet(..., column_styles=, header_style=, column_widths=);write_row(values, style_id=, height=, cell_styles=)CellStyle,Border, enums;Column::style;SheetOptionswithwrite_sheet_with/write_columns_with; derive#[excel(style(bold, number_format = "...", fill_color = 0xFFFF00, ...))], where invalid values are compile errorsAll four bindings rebuild their one-shot write helpers on the writer handle and mark them deprecated ("since 7.4, removed in 8.0"):
[[deprecated]]write_workbook, with aDeprecationWarningwrite_columns(_to)andwrite_sheet(_to), with#[deprecated(since = "7.4.0")]Styles are offered only on the writer-handle APIs.
docs/guide/versioning.mdexplains why the ABI version moved even though the change is additive.Also in this PR:
native-bindings.yml: a newtest-netframeworkjob (Windows) runs the net48 tests and a legacypackages.configconsumer smoke.release.yml: a newnetframeworkjob tests, then packs with the Windows native DLL;publishnow collectsnuget-packages*.CITATION.cffand the Zenodo concept DOI, for APA/BibTeX through GitHub's "Cite this repository".Evidence
Cell styles (Core)
.xlswith exactly 56 colors, and opened them in Excel 16 through COM. 652 read-back values matched with 0 mismatches. They covered font, fill, all 13 border styles, alignment, rotation, indent, row height, styled blank, header style,[ExcelStyle]and parallel write.ABI 7 and bindings
xl_write_typed(XLSX, XLSB, XLS, CSV) and of the 40-bytexl_sheet_optionspath were computed on c226bc8, before any ABI work. All 6 are identical on this tree.xl_end_row;ExcelReader.NET.Framework
Packaging:
tests/ExcelReader.NetFramework.PackageSmoke/run.ps1packs the nupkg, restores it into a legacypackages.configproject and builds it with VS MSBuild. It assertsbin\x64\ExcelReader.Native.dllis present, readsRealExcel.xlsx(100rows), writes, and exits 0.Read performance: net48 x64,
65K_Records_Data, all 14 columns into the same POCO, results identical field by field. Stopwatch A/B, minimum of 5 interleaved bursts.Write performance: 65K rows × 14 columns XLSX takes 45 ms, against 6.8 s for EPPlus 3.1.3.3 (150×).
Test suites on this tree
clippy -D warningscleanMerge Danger
Door: two-way for the merge, one-way at the next release
Merging only adds code, tests and CI jobs, and reverting is clean. The next tagged release makes these permanent:
ExcelReader.NET.Frameworkreaches NuGet for the first time. The package ID and every published version are permanent.CellStyleproperties,ExcelColor,CellBorder, the style enums,IRowWriter.Style,StartRow(int, double),[ExcelStyle],WithHeaderStyleandColumn(..., CellStyle). Post-v7 policy is append-only.xl_cell_styleand the five style exports. The binding APIs too, including the Rust derive'sstyle(...)keys, which use Core's names (horizontal_alignment,vertical_alignment,wrap_text).Blast Radius: CI/release, native-library consumers, and XLS/XLSB output for styled workbooks
Microsoft.DotNet.ILCompiler10.0.13 has a high-severity advisory (GHSA-p3jp-9v83-76w9, NU1903). It compilesExcelReader.Native, which every binding and the new package ship.-p:NuGetAudit=false. BenchmarkDotNet can't build either, which is why the performance numbers come from a Stopwatch harness.-D warningsand call the free write functions will fail to compile until they move toWriterHandle.write_workbooksee aDeprecationWarning.netframeworkrelease job fails,publishis blocked for every package, because it is inneeds.e9f5e2a9) had no independent review.CellStylein a hand-written record map fails asTypeInitializationExceptionon first use. The generator catches the same mistake at compile time asEXR011.PlatformNotSupportedExceptionthat names the missing folder. Enabling x86 needs a decision on the native calling convention: the exports are stdcall on win-x86, while the wrapper declares Cdecl.