Giter Club home page Giter Club logo

largexlsx's Introduction

LargeXlsx - A .net library to write large XLSX files

NuGet

This is a minimalistic yet feature-rich library, written in C# targeting .net standard 2.0, providing simple primitives to write Excel files in XLSX format in a streamed manner, so that potentially huge files can be created while consuming a low, constant amount of memory.

Supported features

Currently the library supports:

  • cells containing inline or shared strings; numeric values; date and time; formulas
  • multiple worksheets
  • merged cells
  • split panes, a.k.a. frozen rows and columns
  • styling such as font face, size, and color; background color; cell borders; numeric formatting; alignment
  • column and row formatting (custom width, height, hidden columns/rows)
  • auto filter
  • cell validation, such as dropdown list of allowed values
  • right-to-left worksheets, to support languages such as Arabic and Hebrew
  • password protection of sheets against accidental modification
  • headers and footers for worksheet printout

Example

To create a basic single-sheet Excel document:

using var stream = new FileStream("Basic.xlsx", FileMode.Create, FileAccess.Write);
using var xlsxWriter = new XlsxWriter(stream);
xlsxWriter
    .BeginWorksheet("Sheet 1")
    .BeginRow().Write("Name").Write("Location").Write("Height (m)")
    .BeginRow().Write("Kingda Ka").Write("Six Flags Great Adventure").Write(139)
    .BeginRow().Write("Top Thrill Dragster").Write("Cedar Point").Write(130)
    .BeginRow().Write("Superman: Escape from Krypton").Write("Six Flags Magic Mountain").Write(126);

To create an Excel document with some fancy formatting:

using var stream = new FileStream("Simple.xlsx", FileMode.Create, FileAccess.Write);
using var xlsxWriter = new XlsxWriter(stream);
var headerStyle = new XlsxStyle(
    new XlsxFont("Segoe UI", 9, Color.White, bold: true),
    new XlsxFill(Color.FromArgb(0, 0x45, 0x86)),
    XlsxStyle.Default.Border,
    XlsxStyle.Default.NumberFormat,
    XlsxAlignment.Default);
var highlightStyle = XlsxStyle.Default.With(new XlsxFill(Color.FromArgb(0xff, 0xff, 0x88)));
var dateStyle = XlsxStyle.Default.With(XlsxNumberFormat.ShortDateTime);
var borderedStyle = highlightStyle.With(
    XlsxBorder.Around(new XlsxBorder.Line(Color.DeepPink, XlsxBorder.Style.Dashed)));

xlsxWriter
    .BeginWorksheet("Sheet 1",
        columns: new[] { XlsxColumn.Unformatted(count: 2), XlsxColumn.Formatted(width: 20) })
    .SetDefaultStyle(headerStyle)
    .BeginRow().AddMergedCell(2, 1).Write("Col1").Write("Top2").Write("Top3")
    .BeginRow().Write().Write("Col2").Write("Col3")
    .SetDefaultStyle(XlsxStyle.Default)
    .BeginRow().Write("Row3").Write(42).WriteFormula(
        $"{xlsxWriter.GetRelativeColumnName(-1)}{xlsxWriter.CurrentRowNumber}*10", highlightStyle)
    .BeginRow().Write("Row4").SkipColumns(1).Write(new DateTime(2020, 5, 6, 18, 27, 0), dateStyle)
    .SkipRows(2)
    .BeginRow().Write("Row7", borderedStyle, columnSpan: 2).Write(3.14159265359)
    .SetAutoFilter(2, 1, xlsxWriter.CurrentRowNumber - 1, 3);

The output is like:

Single sheet Excel document with 7 rows and 3 columns

Table of contents

Changelog

  • 1.9: Optionally force writing cell references for compatibility with some readers, thanks to Mikk182; header and footer functionality thanks to soend
  • 1.8: Ability to hide grid lines and row and column headers from worksheets, thanks to Rajeev Datta
  • 1.7: Write overload for booleans, more performance improvements thanks to Antony Corbett and Mark Pflug
  • 1.6: Opt-in shared string table (for memory vs. file size trade-off)
  • 1.5: Password protection of sheets
  • 1.4: Support for font underline, thanks to Sergey Bochkin
  • 1.3: Optional ZIP64 support for really huge files
  • 1.2: Right-to-left worksheets. Parametric compression level to trade between space and speed
  • 1.1: Number format for text-formatted cells (the "@" formatting)
  • 1.0: Finalized API
  • 0.0.9: Started writing XLSX files directly rather than using Microsoft's Office Open XML library

Usage

The XlsxWriter class is the entry point for almost all functionality of the library. It is designed so that most of its methods can be chained to write the Excel file using a fluent syntax.

The constructor allows you to create an XLSX writer. Please note that an XlsxWriter object must be disposed to properly finalize the Excel file. Sandwitching its lifetime in a using statement is recommended.

// class XlsxWriter
public XlsxWriter(
    Stream stream,
    SharpCompress.Compressors.Deflate.CompressionLevel compressionLevel = CompressionLevel.Level3, // compressionLevel since version 1.2
    bool uzeZip64 = false, // useZip64 since version 1.3
    bool requireCellReferences = true); // requireCellReferences since version 1.9

The constructor accepts:

  • A writeable Stream to save the Excel file into
  • An optional desired compression level of the underlying zip stream. The default CompressionLevel.Level3 roughly matches file sizes produced by Excel. Higher compression levels may result in lower speed.
  • An optional flag indicating whether to use ZIP64 compression to support content larger than 4 GiB uncompressed. Recent versions of XLSX-enabled applications such as Excel or LibreOffice should be able to read any file compressd using ZIP64, even small ones, thus, if you don't know the file size in advance and you target recent software, you could just set it to true.
  • An optional flag indicating whether row numbers and cell references (such as "A1") are to be included in the XLSX file even when redundant. Row numbers and cell references are optional according to the specification, and omitting them provides a notable performance boost when writing XLSX files (as much as 40%). Unfortunately, some non-compliant readers (which apparently include MS Access itself!) consider files without row and cell references as invalid, thus you can be consertative and set this flag to true if you want to make them happy. Spreadsheet applications such as Excel and LibreOffice can read XLSX files without references just fine, thus, if they are your target, you could use false for greater performance

The recipe is adding a worksheet with BeginWorksheet, adding a row with BeginRow, writing cells to that row with Write, and repeating as required. Rows and worksheets are implicitly finalized as soon as new rows or worksheets are added, or the XlsxWriter is disposed.

The insertion point

To enable streamed write, the content of the Excel file must be written strictly from top to bottom and from left to right. Think of an insertion point always advancing when writing content.
The CurrentRowNumber and CurrentColumnNumber read-only properties will return the location of the next cell that will be written. Both the row and column numbers are one-based.

// class XlsxWriter
public int CurrentRowNumber { get; }
public int CurrentColumnNumber { get; }

Please note that CurrentColumnNumber may be zero, and thus invalid, if the current row has not been set up using BeginRow (attempting to write a cell would throw an exception).

Column names

In the usual "A1" cell reference format, columns are named from A (for column number 1) to XFD (for column number 16384).
The following facilitate conversion from column numbers to column names, useful when writing formulas:

// class XlsxWriter
public string CurrentColumnName { get; }
public string GetRelativeColumnName(int offsetFromCurrentColumn);
public static string GetColumnName(int columnIndex);

The first version returns the column name for the column at the insertion point. The second version returns the column name for a column relative to the insertion point. The last version returns the column name for an absolute column index. Absolute or relative indexes outside the range [1..16384] will result in an ArgumentOutOfRangeException.

Creating a new worksheet

Call BeginWorksheet passing the sheet name and one or more of the following optional parameters (using named arguments is recommended):

  • splitRow: if greater than zero, the one-based index of the row where to place a horizontal split to create frozen panes
  • splitColumn: if greater than zero, the one-based index of the column where to place a vertical split to create frozen panes
  • rightToLeft: set to true to switch the worksheet to right-to-left mode (to support languages such as Arabic and Hebrew)
  • columns: pass a non-null list to specify optional column formatting (see below)
  • showGridLines: set to false to hide gridlines in the sheet
  • showHeaders: set to false to hide row and column headers in the sheet
// class XlsxWriter
public XlsxWriter BeginWorksheet(
        string name,
        int splitRow = 0,
        int splitColumn = 0,
        bool rightToLeft = false, // rightToLeft since version 1.2
        IEnumerable<XlsxColumn> columns = null,
        bool showGridLines = true, // showGridLines since version 1.8
        bool showHeaders = true); // showHeaders since version 1.8

Note that, for compatibility with a restriction of the Excel application, names are restricted to a maximum of 31 character. An ArgumentException is thrown if a longer name is passed. An ArgumentException is also thrown when trying to add a worksheet with a name already used for another worksheet.

A call to BeginWorksheet finalizes the last worksheet being written, if any, and sets up a new one, so that rows can be added.

Column formatting

The BeginWorksheet method accepts an optional columns parameter to specify a list of column formatting objects of type XlsxColumn, each describing one or more adjacent columns, with their custom width, hidden state or default style, starting from column A.

This information must be provided before writing any content to the worksheet, thus the number, width and styles of the columns must be known in advance.

You can create XlsxColumn objects with one of these named constructors:

// class XlsxColumn
public static XlsxColumn Unformatted(int count = 1);
public static XlsxColumn Formatted(double width, int count = 1, bool hidden = false, XlsxStyle style = null);

Unformatted creates a column description that is used basically to skip one or more unformatted columns.

Formatted creates a column description to specify the mandatory witdh, optional hidden state, and optional style of one or more contiguous columns. The width is expressed (simplyfing) in approximate number of characters. The column style represents how to style all empty cells of a column. Cells that are explicitly written always use the cell style instead.

Adding or skipping rows

Call BeginRow to advance the insertion point to the beginning of the next line and set up a new row to accept content. If a previous row was being written, it is finalized before creating the new one.

// class XlsxWriter
public XlsxWriter BeginRow(double? height = null, bool hidden = false, XlsxStyle style = null);

You can specify optional row formatting when creating a new row. The height is expressed in points. The row style represent how to style all empty cells of a row. Cells that are explicitly written always use the cell style instead.

Call SkipRows to move the insertion point down by the specified count of rows, that will be left empty and unstyled (unless column styles are in place). If a previous row was being written, it is finalized. Please note that BeginRow must be called anyways before starting to write a new row.

// class XlsxWriter
public XlsxWriter SkipRows(int rowCount);

Writing cells

Call one of the Write methods to write content to the cell at the insertion point:

// class XlsxWriter
public XlsxWriter Write(XlsxStyle style = null, int columnSpan = 1, int repeatCount = 1);
public XlsxWriter Write(string value, XlsxStyle style = null, int columnSpan = 1);
public XlsxWriter Write(double value, XlsxStyle style = null, int columnSpan = 1);
public XlsxWriter Write(decimal value, XlsxStyle style = null, int columnSpan = 1);
public XlsxWriter Write(int value, XlsxStyle style = null, int columnSpan = 1);
public XlsxWriter Write(DateTime value, XlsxStyle style = null, int columnSpan = 1);
public XlsxWriter Write(bool value, XlsxStyle style = null, int columnSpan = 1); // since version 1.7
public XlsxWriter WriteFormula(string formula, XlsxStyle style = null, int columnSpan = 1, IConvertible result = null);
public XlsxWriter WriteSharedString(string value, XlsxStyle style = null, int columnSpan = 1); // since version 1.6

You may write one of the following:

  • Nothing: a cell containing no value, but styled nonetheless.
  • String: an inline literal string of text; if the string is null the method falls back on the "nothing" case. The value of an inline string is written into the cell, thus resulting in low memory consumption but possibly larger files (see, in contrast, shared strings).
  • Number: a numeric constant, that will be interpreted as a double value; convenience overloads accepting int and decimal are provided, but under the hood the value will be converted to double because it is the only numeric type truly supported by the XLSX file format.
  • Date and time: a DateTime value, that will be converted to its double representation (days since 1900-01-01). Note that you must style the cell using a date/time number format to have the value appear as a date.
  • Boolean: a bool value, that will appear either as TRUE or FALSE
  • Formula: a string that Excel or a compatible application will interpret as a formula to calculate. Note that, unless you provide a result calculated by yourself (either string or numeric), no result is saved into the XLSX file. However, a spreadsheet application will calculate the result as soon as the XLSX file is opened.
  • Shared string: a shared literal string of text. Contrary to inline strings, shared strings are saved in a look-up table and deduplicated in constant time, and only a reference is written into the cell. This may help to produce smaller files, but, since the shared strings must be accumulated in RAM, writing a large number of different shared strings may cause high memory consumption. Keep this in mind when choosing between inline strings and shared strings.

The style parameter specifies the style to use for the cell being written. If null (or omitted), the cell is styled using the current default style of the XlsxWriter (see Styling). Note that in no case the style of the column or the row, if any, is used for written cells.

When writing empty cells you can also specify a repeatCount parameter, to write multiple consecutive styled empty cells. Note that the difference between repeatCount and columnSpan both greater than 1 is that the latter creates a merged cell (with its memory consumption drawback) and does not have in-between borders.

The columnSpan parameter can be used to let the cell span multiple columns. When greater than 1, a merged range of such cells is created (see Merged cells), content is written to the first cell of the range and the insertion point is advanced after the merged cells. Note that xlsxWriter.Write(value, columnSpan: count) is actually a shortcut for xlsxWriter.AddMergedCells(1, count).Write(value).Write(repeatCount: count - 1). Since a merged cell is created, writing a large number of cells with columnSpan greater than 1 may cause high memory consumption.

Writing hyperlinks

The XLSX file format provides two ways to insert hyperlinks (either to a web site or cells perhaps in a differet workbook): using a specific "hyperlinks" section in the worksheet and using the HYPERLINK formula in a cell.

The former, which is the one used by the "Insert hyperlink" command in Excel, is not used by this library, because it would require to accumulate all hyperlinks in RAM until the worksheet is complete.

The latter can be used with WriteFormula while streaming content into the worksheet, thus is more appropriate for the use case of this library, for example:

xlsxWriter.WriteFormula(
    "HYPERLINK(\"https://github.com/salvois/LargeXlsx\", \"LargeXlsx on GitHub\")",
    XlsxStyle.Default.With(XlsxFont.Default.WithUnderline().With(Color.Blue));

where the first parameter of the HYPERLINK formula is the link location and the second parameter, which is optional, is a friendly name to display into the cell. Styling may be used to show the cell contains a link.

Skipping columns

Like rows, cells can be skipped using the SkipColumns method, to move the insertion point to the right by the specified count of cells, that will be left empty and unstyled (unless column or row styles are in place).

// class XlsxWriter
public XlsxWriter SkipColumns(int columnCount);

Merged cells

A rectangle of adjacent cells can be merged using the AddMergedCells method:

// class XlsxWriter
public XlsxWriter AddMergedCell(int fromRow, int fromColumn, int rowCount, int columnCount);
public XlsxWriter AddMergedCell(int rowCount, int columnCount);

The first overload lets you specify an arbitrary rectangle in the worksheet.
The second overload facilitates merging cells while fluently writing the file, using the insertion point as the top-left cell for the merged range.

Creating a merged cell range does not advance the insertion point.
As a shortcut for the common case of merging a 1 row by n columns range, you can pass a value greater than 1 as the columnSpan argument of a Write method, that will create the merged cell and advance the insertion point for you.

Content for the merged cells must be written in the top-left cell of the rectangle. A spreadsheet application will not display any content of the remaining cells in the merged range. Thus, you should explicitly skip those cells using SkipColumns, SkipRows and writing empty cells (if styling is needed) as appropriate.
For example, if merging the 2 rows x 3 columns range A7:C8 using AddMergedCells(7, 1, 2, 3), you must write content for the merged cell in A7, then explicitly jump by further 2 columns using SkipColumns(2) to continue writing content from D7, and the same applies on row 8, where after a BeginRow() you must skip 3 columns with SkipColumns(3) and continue writing from D8.

Note: due to the structure of the XLSX file format, the ranges for all merged cells of a worksheet must be accumulated in RAM, because they must be written to the file after the content of the whole worksheet. Using a large number of merged cells may cause high memory consumption. This also means that you may call AddMergedCell at any moment while you are writing a worksheet (that is between a BeginWorksheet and the next one, or disposal of the XlsxWriter object), even for cells already written or well before writing them, or cells you won't write content to.

Auto filter

You can add an auto filter (the one created with the funnel icon in Excel) for a specific rectangular region, containing headers in the first row, using:

// class XlsxWriter
public XlsxWriter SetAutoFilter(int fromRow, int fromColumn, int rowCount, int columnCount);

You can call SetAutoFilter at any moment while writing a worksheet (that is between a BeginWorksheet and the next one, or disposal of the XlsxWriter object). Each worksheet can contain only up to one auto filter, thus if you call SetAutoFilter multiple times for the same worksheet only the last one will apply.

Data validation

Data validation lets you add constraints on cell content. Such constraints are represented by XlsxDataValidation objects, created with:

// class XlsxDataValidation
public XlsxDataValidation(
        bool allowBlank = false,
        string error = null,
        string errorTitle = null,
        XlsxDataValidation.ErrorStyle? errorStyle = null,
        XlsxDataValidation.Operator? operatorType = null,
        string prompt = null,
        string promptTitle = null,
        bool showDropDown = false,
        bool showErrorMessage = false,
        bool showInputMessage = false,
        XlsxDataValidation.ValidationType? validationType = null,
        string formula1 = null,
        string formula2 = null);

Using named arguments is recommended to improve readability. The parameters represent:

  • allowBlank: whether an empty cell is considered valid
  • error: an optional error message to replace the default message of the spreadsheet application when invalid content is detected
  • errorTitle: an optional title to replace the default title of the spreadsheet application when invalid content is detected
  • errorStyle: whether to report detection of invalid content as a blocking error, a warning or a notice
  • operatorType: for validation involving comparison (see validationType), the comparison operator to apply
  • prompt: an optional message to be shown as tooltip when the cell receives focus
  • promptTitle: an optional title to show in the prompt tooltip
  • showDropDown: whether to show a dropdown list when the validation type is set to XlsxDataValidation.ValidationType.List; Note: for some reason, this seems to work backwards, with both Excel and LibreOffice showing the dropdown when this property is false
  • showErrorMessage: whether the spreadsheet application will show an error when invalid content is detected
  • showInputMessage: whether the spreadsheet application will show the prompt tooltip when a validated cell is focused
  • validationType: specifies to do validation on numeric values, whole integer values, date values, text length (using the comparison operator specified by operatorType) or against a list of allowed values
  • formula1: the value to compare the cell content against; for lists, it can be a reference to a cell range containing the allowed values, or a list of comma separated case-sensitive string contants, enclosed by double quotes (e.g. "\"item1,item2\""); for other validation types, it can be a reference to a cell containing the value or a constant
  • formula2: for comparison involving two values (e.g. operator Between), the second value, specified as in formula1.

To ease creation of an XlsxDataValidation specifying validation against a list of string contants, the following named constructor is provided. Note that it basically does a string.Join of choices into formula1, thus if a choice includes a comma, the spreadsheet application will split it in separate choices. There is no way to include real commas in choices specified as string constants (rather than as cell range).

// class XlsxDataValidation
public static XlsxDataValidation List(
        IEnumerable<string> choices,
        bool allowBlank = false,
        string error = null,
        string errorTitle = null,
        XlsxDataValidation.ErrorStyle? errorStyle = null,
        string prompt = null,
        string promptTitle = null,
        bool showDropDown = false,
        bool showErrorMessage = false,
        bool showInputMessage = false);

To add validation rules to a worksheet, use one of the following while writing content:

// class XlsxWriter
public XlsxWriter AddDataValidation(int fromRow, int fromColumn, int rowCount, int columnCount,
                                    XlsxDataValidation dataValidation);
public XlsxWriter AddDataValidation(int rowCount, int columnCount, XlsxDataValidation dataValidation);
public XlsxWriter AddDataValidation(XlsxDataValidation dataValidation);

The first overload applies the validation rules to all cells in the specified rectangular range. The second overload uses the insertion point as the top-left corner of the rectangular range. The third overload applies validation only on the cell at the insertion point.

Note that, due to the internals of the XLSX file format, all validation objects and their cell references must be kept in RAM until a worksheet is finalized, but this library deduplicates validation objects in constant time as needed. Thus, you should usually not worry about performance or memory consumption when you use multiple validation objects, unless you are using a large number of different ones, or specify a lot of separate cell references.

This means that you may call AddDataValidation at any moment while you are writing a worksheet (that is between a BeginWorksheet and the next one, or disposal of the XlsxWriter object), even for cells already written or well before writing them, or cells you won't write content to.

Password protection of sheets

Password protection of worksheets helps preventing accidental modification of data. You can enable protection on the worksheet being written using:

// class XlsxWriter
public XlsxWriter SetSheetProtection(XlsxSheetProtection sheetProtection); // since version 1.5

// class XlsxSheetProtection
public XlsxSheetProtection(
        string password,
        bool sheet = true,
        bool objects = true,
        bool scenarios = true,
        bool formatCells = true,
        bool formatColumns = true,
        bool formatRows = true,
        bool insertColumns = true,
        bool insertRows = true,
        bool insertHyperlinks = true,
        bool deleteColumns = true,
        bool deleteRows = true,
        bool selectLockedCells = false,
        bool sort = true,
        bool autoFilter = true,
        bool pivotTables = true,
        bool selectUnlockedCells = false);

Each flag in XlsxSheetProtection specifies what operations are protected, that is not allowed. By default, only selecting cells is allowed. The password must have a length between 1 and 255 characters.

You can call SetSheetProtection at any moment while writing a worksheet (that is between a BeginWorksheet and the next one, or disposal of the XlsxWriter object). Each worksheet can contain only up to one protection definition, thus if you call SetSheetProtection multiple times for the same worksheet only the last one will apply.

Note: password protection of sheets is not to be confused with workbook encryption and is not meant to be secure. File contents are still written in clear text and may be changed by deliberately editing the file. The password is not written into the file but a hash of the password is. Workbook encryption (that is, password protection of the whole file) requires the writer to use a different file format and will be, unfortunately, not provided by this library in the foreseeable future.

Headers and footers

Call SetHeaderFooter on an XlsxWriter when you want to add headers and footers to worksheet printout (since version 1.9):

//class XlsxWriter
public XlsxWriter SetHeaderFooter(XlsxHeaderFooter headerFooter);

//class XlsxHeaderFooter
public XlsxHeaderFooter(
        string oddHeader = null,
        string oddFooter = null,
        string evenHeader = null,
        string evenFooter = null,
        string firstHeader = null,
        string firstFooter = null,
        bool alignWithMargins = true,
        bool scaleWithDoc = true);

You can call SetHeaderFooter at any moment while writing a worksheet (that is between a BeginWorksheet and the next one, or disposal of the XlsxWriter object). Each worksheet can contain only up to one header/footer definition, thus if you call SetHeaderFooter multiple times for the same worksheet only the last one will apply.

The constructor of XlsxHeaderFooter takes several properties to control the content of headers or footers:

  • oddHeader sets the header text, if any, to use on every page, or only on odd pages if evenHeader is also set
  • oddFooter sets the footer text, if any, to use on every page, or only on odd pages if evenFooter is also set
  • firstHeader sets the header text, if any, to be used only on first page
  • firstFooter sets the footer text, if any, to be used only on first page
  • evenHeader sets the header text, if any, to be used only on even pages
  • evenFooter sets the footer text, if any, to be used only on even pages
  • alignWithMargins: align header and footer margins with page margins. When true, as left/right margins grow and shrink, the header and footer edges stay aligned with the margins. When false, headers and footers are aligned on the paper edges, regardless of margins
  • scaleWithDoc: scale header and footer with document scaling

Note: that header and footer text may contain a number of formatting codes to control styling and insert special content such as the page number or file name. These codes are prefixed with the & character (more information below). A single literal ampersand must thus be represented as &&.

Left, center and right sections of header and footer

Spreadsheet applications such as Excel and LibreOffice expect headers and footers to be formed of three different sections, for left-, center- and right-aligned texts.

These sections are introduced by the &L, &C and &R formatting codes respectively. For maximum compatibility, you should always use at least one of them to specify at least one section, and you should not repeat the same sections multiple times (although supported, this could lead to unexpected results)-

Other libraries force you to follow the above recommendation by providing an object with three separate properties rather than a single string. This library does not enforce this by design, to facilitate localization in right-to-left cultures.

Header and footer formatting codes

Excel specifies a set of formatting codes to control styling and insert special content into headers and footers, as specified in Office Implementation Information for ISO/IEC 29500 Standards Support.

You can either insert these formatting codes manually in your header and footer strings, to ease localization, or use the following class to build header and footer strings programmatically:

public class XlsxHeaderFooterBuilder
{
    public override string ToString(); // renders the built text

    public XlsxHeaderFooterBuilder Left(); // &L - left section
    public XlsxHeaderFooterBuilder Center(); // &C - center section
    public XlsxHeaderFooterBuilder Right(); // &R - right section
    public XlsxHeaderFooterBuilder Text(string text); // plain text, auto escaping ampersands
    public XlsxHeaderFooterBuilder CurrentDate(); // &D
    public XlsxHeaderFooterBuilder CurrentTime(); // &T
    public XlsxHeaderFooterBuilder FileName(); // &F
    public XlsxHeaderFooterBuilder FilePath(); // &Z
    public XlsxHeaderFooterBuilder NumberOfPages(); // &N
    public XlsxHeaderFooterBuilder PageNumber(int offset = 0) // &P or &P+offset or &P+offset - current page number, optionally offset by the specified number
    public XlsxHeaderFooterBuilder SheetName(); // &A
    public XlsxHeaderFooterBuilder FontSize(int points); // &points
    public XlsxHeaderFooterBuilder Font(string name, bool bold = false, bool italic = false); // &"name,type" - set font name and type
    public XlsxHeaderFooterBuilder Font(bool bold = false, bool italic = false); // &"-,type" - set only font type
    public XlsxHeaderFooterBuilder Bold(); // &B - each occurrence toggles on or off
    public XlsxHeaderFooterBuilder Italic(); // &I - each occurrence toggles on or off
    public XlsxHeaderFooterBuilder Underline(); // &U - each occurrence toggles on or off
    public XlsxHeaderFooterBuilder DoubleUnderline(); // &E - each occurrence toggles on or off
    public XlsxHeaderFooterBuilder StrikeThrough(); // &S - each occurrence toggles on or off
    public XlsxHeaderFooterBuilder Subscript(); // &Y - each occurrence toggles on or off
    public XlsxHeaderFooterBuilder Superscript(); // &X - each occurrence toggles on or off
}

When writing the &"font,type" and &"-,type" codes manually, the type field can assume one of the following values: Regular, Bold, Italic and Bold Italic.

Styling

Styling lets you apply colors or other formatting to cells being written. A style is made up of five components:

  • the font, including face, size and text color, represented by an XlsxFont object
  • the fill, specifying the background color, represented by an XlsxFill object
  • the border style and color, represented by an XlsxBorder object
  • the number format specifying how a number should appear, such as how many decimals or whether to show it as percentage or date and time, represented by an XlsxNumberFormat object
  • the alignment, specifying horizontal and vertical alignment of cell content or rotation, represented by an XlsxAlignment object.

You can create a new style, combining the above five elements, using the constructor of the XlsxStyle class, or by cloning an existing style replacing one element with a With method:

// class XlsxStyle
public XlsxStyle(XlsxFont font, XlsxFill fill, XlsxBorder border, XlsxNumberFormat numberFormat,
                 XlsxAlignment alignment);
public XlsxStyle With(XlsxFont font);
public XlsxStyle With(XlsxFill fill);
public XlsxStyle With(XlsxBorder border);
public XlsxStyle With(XlsxNumberFormat numberFormat);
public XlsxStyle With(XlsxAlignment alignment);

The resulting XlsxStyle object can be used with a Write method to style a cell being written, or with SetDefaultStyle to change the default style of the XlsxWriter, or to specify column or row styles.

Under the hood, all styles used with an XlsxWriter are collected in a stylesheet, and this library deduplicates them in constant time as needed. Thus, you should usually not worry about performance or memory consumption when you use multiple styles. Note, however, that the stylesheet is kept in RAM until the XlsxWriter is disposed, thus using a very large number of different styles may cause high memory consumption.

The built-in XlsxStyle.Default object provides a ready-to-use style combining the built-in XlsxFont.Default font, the built-in XlsxFill.None fill, the built-in XlsxBorder.None border, the built-in XlsxNumberFormat.General number format and the built-in XlsxAlignment.Default alignment. All those elements are explained later.

The default style

Each XlsxWriter has a default style that is used whenever you write a cell using Write without specifying an explicit style. To read the current default style, or set it while fluently write the XLSX file, use:

// class XlsxWriter
public XlsxStyle DefaultStyle { get; }
public XlsxWriter SetDefaultStyle(XlsxStyle style);

The default style of a new XlsxWriter is set to XlsxStyle.Default, but you can change it at any time, including setting it back to XlsxStyle.Default.

Fonts

An XlsxFont object lets you define the font face, its size in points, the text color and emphasis such as bold, italic and strikeout. Create it via constructor, or clone an existing one replacing a property with a With method:

// class XlsxFont
public XlsxFont(
        string name,
        double size,
        System.Drawing.Color color,
        bool bold = false,
        bool italic = false,
        bool strike = false,
        XlsxFont.Underline underline = XlsxFont.Underline.None); // underline since version 1.4
public XlsxFont With(System.Drawing.Color color);
public XlsxFont WithName(string name);
public XlsxFont WithSize(double size);
public XlsxFont WithBold(bool bold = true);
public XlsxFont WithItalic(bool italic = true);
public XlsxFont WithStrike(bool strike = true);
public XlsxFont WithUnderline(XlsxFont.Underline underline = XlsxFont.Underline.None); // since version 1.4

XlsxFont.Underline enum provides underline styles None, Single, Double, SingleAccounting and DoubleAccounting.

Using named arguments is recommended to improve readability. For example to create a red, italic, 11-point, Calibri font, use:
var redItalicFont = new XlsxFont("Calibri", 11, Color.Red, italic: true).

The built-in XlsxFont.Default object provides a default black, plain, 11-point, Calibri font.

Fills

An XlsxFill object lets you define the background color of a cell and the pattern to use to fill the background. Create it with:

// class XlsxFill
public XlsxFill(System.Drawing.Color color, XlsxFill.Pattern patternType = XlsxFill.Pattern.Solid);

The XlsxFill.Pattern enum defines how to apply the background color, and may be None for a transparent fill, Solid for a solid fill or Gray125 for a dotted pattern (not necessarily gray) with 12.5% coverage. For example to create a yellow solid fill, use:
var yellowFill = new XlsxFill(Color.Yellow)

The built-in XlsxFill.None object provides a default empty fill.

Borders

An XlsxBorder object lets you define thickness and colors for the borders of a cell. Create a new border set via one of the following:

// class XlsxBorder
public XlsxBorder(
        XlsxBorder.Line top = null,
        XlsxBorder.Line right = null,
        XlsxBorder.Line bottom = null,
        XlsxBorder.Line left = null,
        XlsxBorder.Line diagonal = null,
        bool diagonalDown = false,
        bool diagonalUp = false);
public static XlsxBorder Around(XlsxBorder.Line around);

The constructor lets you specify each border individually. Using named arguments is recommended to improve readability. The two diagonal borders share the same line style, but you can choose whether to show them individually. The Around named constructor is a shortcut for the common case of setting the top, right, bottom and left borders to the same line style, with no diagonals.

Each line is constructed with:

// class XlsxBorder.Line
public Line(System.Drawing.Color color, XlsxBorder.Style style);

The XlsxBorder.Style enum defines the stroke style, such as None, Thin, Thick, Dashed and others.

For example to create a thin black border on the left side only, use:
var leftBorder = new XlsxBorder(left: new XlsxBorder.Line(Color.Black, XlsxBorder.Style.Thin)).

The built-in XlsxBorder.None object provides a default empty border set.

Number formats

An XlsxNumberFormat object lets you define how the content of a cell should appear when it contains a numeric value. Create it via constructor:

// class XlsxNumberFormat
public XlsxNumberFormat(string formatCode);

The format code has the same format you would normally use in Excel, such as "0.0%" for a percentage with exactly one decimal value. For example, to create a custom number format with thousand separator, at least two decimal digits and at most six, use:
var customNumberFormat = new XlsxNumberFormat("#,##0.00####").

Excel defines and reserves many "magic" number formats, and this library exposes some of them as:

  • XlsxNumberFormat.General: the default number format, where Excel automatically chooses the "best" representation based on magnitude and number of decimals.
  • XlsxNumberFormat.Integer: no decimal digits, that is the format code "0".
  • XlsxNumberFormat.TwoDecimal: two decimal digits, that is the format code "0.00".
  • XlsxNumberFormat.ThousandInteger: thousand separators and no decimal digits, that is the format code "#,##0".
  • XlsxNumberFormat.ThousandTwoDecimal: thousand separators and two decimal digits, that is the format code "#,##0.00".
  • XlsxNumberFormat.IntegerPercentage: percentage formatting and no decimal digits, that is the format code "0%".
  • XlsxNumberFormat.TwoDecimalPercentage: percentage formatting and two decimal digits, that is the format code "0.00%".
  • XlsxNumberFormat.Scientific: scientific notation with two decimals and two-digit exponent, that is the format code "0.00E+00".
  • XlsxNumberFormat.ShortDate: localized day, month and year as digits; for a European format the equivalent code would be "dd/mm/yyyy" but the actual code would be locale-dependent.
  • XlsxNumberFormat.ShortDateTime: localized day, month and year as digits with hours and minutes; for a European format the equivalent code would be "dd/mm/yyyy hh:mm" but the actual code would be locale-dependent.
  • XlsxNumberFormat.Text: treat newly inserted numbers as text, that is the format code "@" (since version 1.1).

Alignment

An XlsxAlignment object describes alignment and other text control properties, constructed with:

public XlsxAlignment(
        XlsxAlignment.Horizontal horizontal = XlsxAlignment.Horizontal.General,
        XlsxAlignment.Vertical vertical = XlsxAlignment.Vertical.Bottom,
        int indent = 0,
        bool justifyLastLine = false,
        XlsxAlignment.ReadingOrder readingOrder = XlsxAlignment.ReadingOrder.ContextDependent,
        bool shrinkToFit = false,
        int textRotation = 0,
        bool wrapText = false);

Using named arguments is recommended to improve readability. The parameters represent:

  • horizontal: horizontal alignment of the text, such as left, right, center or justified
  • vertical: vertical alignment of the text, such as top, bottom, center or justified
  • indent: how many spaces the cell content must be indented
  • justifyLastLine: whether to justify even the last line when the alignment is set to Justify
  • readingOrder: the text direction such as left-to-right or right-to-left
  • shrinkToFit: whether to reduce automatically the font size to fit the content into the cell
  • textRotation: rotation angle in degrees of the cell content, in range 0..180
  • wrapText: whether to insert line breaks automatically into the text to fit the content into the cell

The built-in XlsxAlignment.Default object provides an alignment with default values for all properties.

Special thanks

Kudos to Roberto Montinaro, Matteo Pierangeli and Giovanni Improta for their patience and their very valuable suggestions! <3

Many thanks to all great people who contributed with pull requests, questions and reports!

License

Permissive, 2-clause BSD style

LargeXlsx - Minimalistic .net library to write large XLSX files

Copyright 2020-2024 Salvatore ISAJA

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

largexlsx's People

Contributors

antonycorbett avatar markpflug avatar piwonesien avatar rajeevdatta avatar rflipper avatar salvois avatar soend avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

largexlsx's Issues

Excel reporting file as faulty, when colspan larger than 2 used on any cell - from 1.9.0

Hi there,
We have discovered issue occurring in newest version (1.9.0).

When you prepare an xlsx with cell, using colspan larger than 2, Excel reports it as faulty and intends to repair it upon opening.
This problem wasn't present in previous version (1.8.0).
We are using you nuget package in solution that runs on linux server (Debian 11 currently).

Belowe you have test code and example excel file.

  using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Write))
  using (var xlsxWriter = new XlsxWriter(stream))
  {

      xlsxWriter.BeginWorksheet("Test");
      xlsxWriter.BeginRow();
      xlsxWriter.BeginRow();

      xlsxWriter.Write("");
      xlsxWriter.Write("Broken", null, 3);

  }

testExcel_20240124_175629.xlsx

Add ZIP64 support for huge files

Hello!

Thanks for such a cool little library.

I got an exception when trying to write a huge file.

System.NotSupportedException: "Attempted to write a stream that is larger than 4GiB without setting the zip64 option"
  at SharpCompress.Writers.Zip.ZipWriter.ZipWritingStream.Write(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
at System.IO.StreamWriter.Write(String value)
at System.IO.StreamWriter.Write(String format, Object[] arg)
at LargeXlsx.Worksheet.Write(String value, XlsxStyle style)
at LargeXlsx.XlsxWriter.DoOnWorksheet(Action action)
at testApp.Program.Main(String[] args) in /.../csharp/testApp/Program.cs:49

I have not found a parameter on using the zip64 flag in the library API.
Can you tell me how to solve this problem?

I used .NET Core 5 version. Also tried to use .NET 4.6.2 on a Windows machine - same exception.
Version library - latest (1.2.0).

โฏ dotnet --info
.NET SDK (reflecting any global.json):

Version:   5.0.100
 Commit:    5044b93829

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  10.15
 OS Platform: Darwin
 RID:         osx.10.15-x64
 Base Path:   /usr/local/share/dotnet/sdk/5.0.100/

Host (useful for support):
  Version: 5.0.0
  Commit:  cf258a14b7

.NET SDKs installed:
  3.1.404 [/usr/local/share/dotnet/sdk]
  5.0.100 [/usr/local/share/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 3.1.10 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.10 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

Not possible to recover files with Excel v. 2110

Hi @salvois
Your library worked great with older excel version, but today I updated it and right now I can't open any of the xlsx files created with LargeXlsx. When trying the option "Open and repair" I'm getting "The file is corrupt and cannot be opened" error. Could take a look at this?

Excel:
Microsoftยฎ Excelยฎ for Microsoft 365 MSO (Version 2110 Build 16.0.14527.20270) 64-bit

.Net

dotnet --info   
.NET SDK (reflecting any global.json):
 Version:   5.0.402
 Commit:    e9d3381880

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.19043
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\5.0.402\

Host (useful for support):
  Version: 5.0.11
  Commit:  f431858f8b

.NET SDKs installed:
  5.0.402 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Broken Excel fail generated when writing to not seekable stream

Hi,

I've tried to use this library to stream Excel files on the fly via ASP.NET Core. My use case prohibits me with caching it in memory and I end up using the raw HTTP response stream. The file generated seems ok at first, but when I load it with Excel it tries to repair it and without any data loss the file is opened.
The only difference between a correct file and the broken one is that the underlying ZIP file has the general purpose bit flag set to (bit 3).
I know that this is more of an Excel issue but did anyone have any luck working around that?

Protection of sheets

Hi @salvois,
thanks for the great library, its actually working really great in some of my testing.

I know this library will not have all the features in favor of performance and I'm happy about that ^^
The main feature I'm missing right now would be protecting an excel sheet with a password. Do you know if that would be possible somehow?

Thanks
Michael

Error exporting data to MS Access

Hello!
I'm trying to create a new data source in ms access from an xlsx file that I created using LargeXlsx.
image

But I get the following error:
image

If I just open xlsx in ms excel and resave it (without editing), then the data source is created successfully after that.

Creating column span using Write(value, style, span) method produces invalid xlsx unless SkipColumns(n - 2) called

Task:
I want to have multi column spanned headers for table

The code for reproducing bug:

using (var stream = new FileStream(reportOutputFilePath, FileMode.Create, FileAccess.Write))
using (var writer = new XlsxWriter(stream, requireCellReferences: false))
{
    writer.BeginWorksheet("Test").BeginRow();
    writer.Write("first", XlsxStyle.Default, 3);
    writer.Write("Second", XlsxStyle.Default, 4);
}

Problem:
Excel complains with error:
"We found a problem with some content in "output2.xlsx". Do you want us to try to recover as much as we can? If you trust.. click Yes"

If I click "Yes" here is what "fixed" looks like:
image

Things of note:
It requires me to set requireCellReferences = false, else i get same error no matter what

Solution:

using (var stream = new FileStream(reportOutputFilePath, FileMode.Create, FileAccess.Write))
using (var writer = new XlsxWriter(stream, requireCellReferences: false))
{
    writer.BeginWorksheet("Test").BeginRow();
    writer.Write("first", XlsxStyle.Default, 3).SkipColumns(1);
    writer.Write("Second", XlsxStyle.Default, 4).SkipColumns(1); // This trailing one can be anything since we don't write after it
}

The value of skipped columns must be columnSpan - 2 for it to work.
Skipping columns less than N-2 causes error, skipping more, works as intended (moving insertion point forward)

Can someone help me with this, is this bug? is this normal? am I doing it wrong? is the new excel just picky?

Also here is XML of the error:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<recoveryLog xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <logFileName>error320960_08.xml</logFileName>
    <summary>Errors were detected in file 'C:\Workspace\output2.xlsx'</summary>
    <removedRecords>
        <removedRecord>Removed Records: Cell information from /xl/worksheets/sheet1.xml part</removedRecord>
        <removedRecord>Removed Records: Merge cells from /xl/worksheets/sheet1.xml part</removedRecord>
    </removedRecords>
</recoveryLog>

WriteCellRef only if _needsRef is set to true

Hello,

I need to write a Xlsx file with row and cell references (A1, B2, C15, ...).

The only way i found to do this is to call :

  • SkipRows(0) before BeginRow() to write row reference
  • SkipColumns(0) before Write() to add cell reference

I would like to tell LargeXlsx to write rows and cells reference for an entire sheet or workbook.

Suggestion :
Add a bool argument in BeginWorksheet function (maybe false by default) and pass it to Worksheet constructor to set a property to manage this behaviour
Warning : be carreful about managing this new feature with the 'needsRef' existing one.

Is it possible to use an existing excel file?

Hey,
is it possible to fill data into an existing excel file or starts this library always from scratch with an empty excel file?

So, could I do something like this?

File.Copy("Template.xlsx", "data.xlsx");
using var stream = new FileStream("data.xlsx", FileMode.Open, FileAccess.ReadWrite);
using var xlsxWriter = new XlsxWriter(stream);
xlsxWriter
    .BeginWorksheet("Sheet 1")
    .SkipRows(2); // Template headers

foreach(var row in datarows)
{
   xlsxWriter.BeginRow();
   foreach(var cell in row){
      xlsxWriter.Write(cell);
   }
}

Feature Request: Writing boolean values

Thank you for providing this library. I am using it with F#. It works great and dealing with large files was really painful before.

Would it be possible to implement a XlsxWriter.Write(bool) method?

Thanks

Plans for in-Cell Rich Text support?

Hello! Your library is absolutely wonderful, and I've had a blast with how powerful and intuitive it is to understand! For collaboration/documentation/feedback reasons, I am currently working a tool to dump the text and generate a .xlsx for a game project that I'm developing on a team.

I am looking for solutions to change the color of my text mid-cell, from white, to the other color, and then back to white, although it seems to be unsupported with this library. I am also experiencing linebreak-related issues, although I used the workaround mentioned in another user's Issue for that.

Are there plans to implement in-Cell Rich text support? I think it would be a very useful feature to have ๐Ÿ™

Thank you for your time, and for the library, too!

Automatic column width adjustment

Hi! Are there any plans regarding the implementation of an automatic column width adjustment?
I'm talking about something similar to AutoFitColumns in EPPlus.
I understand that the way this package is designed requires user to manually provide column width in BeginWorksheet method call. However, described feature seems rather handy.
If, by any chance, you have an idea how to implement it, it would be really great.

Q&A : DateTime format column

I want to use LargeXlsx 1.7.3 to write large data.
I also want format datetime column look like "yyyy/MM/dd HH:mm:ss" , how to format it ?
Thanks

RTL worksheet ?

Hi ,
Thank you for sharing this library.
Is it possible to change the worksheet reading order to rtl ?
also, another question off topic , I think the files generated with this library are considerably bigger than they would if they'd be generated with OpenXML (?), right now I have 400K~ records at about 10MB , while OpenXML is around 2.5MB.

Thank you.

The exported excel is too large

Thanks for the great work! I tried to use this, and it really solved the OOM issue caused by the OpenXML SAX. However, the exported excel was too large - using OpenXML SAX, the file was about 240MB, but using LargeXlsx, it was about 1G. Please tell me how to reduce the size. Thank you.

By the way, I have already removed all styles, and the initialize of xlsxWriter I use is :
using (var xlsxWriter = new XlsxWriter(stream, SharpCompress.Compressors.Deflate.CompressionLevel.BestCompression, true))

Generated *.xlsx not always deemed as proper excel files.

Hi ๐Ÿ™‹โ€โ™€๏ธ,

I'm using your awesome LargeXlsx library (version 1.6.3) with .NET 7.0.101 to generate SAP journal entries files that are then uploaded to SAP (accordingly to their their doc).

Expand / Collapse the C# implementation

AccrualsGenerator.cs:

using LargeXlsx;
using SharpCompress.Compressors.Deflate;

namespace ExcelMeh;

public static class AccrualsGenerator
{
    public static void Generate(string path)
    {
        using var stream = new FileStream(path, FileMode.Create, FileAccess.Write);
        using var xlsxWriter = new XlsxWriter(stream, CompressionLevel.BestCompression, true);

        xlsxWriter.SetDefaultStyle(Constants.Styles.Default)
            .BeginWorksheet(Constants.Values.WorksheetName, columns: Constants.ColumnsMin)
            .BeginRow()
                .Write(Constants.Values.Title, Constants.Styles.Title)
            .BeginRows(Constants.Values.Comments, Constants.Styles.Bold)
            .BeginRow()
                .Write(Constants.Values.BatchId, Constants.Styles.DarkYellowBg)
                .Write(Constants.Styles.DarkYellowBg)
            .SkipRows(2)
            .BeginRow(style: Constants.Styles.DarkBlueBoldBgLeft)
                .Write(1, Constants.Styles.DarkBlueBoldBg)
                .WriteSharedString(Constants.Values.Header, Constants.Styles.DarkBlueBoldBg)
            .BeginRow()
                .SkipColumns(1).WriteSharedStrings(Constants.Values.HeaderColumNamesMin, Constants.Styles.LightBlueBg)
            .BeginRow()
                .SkipColumns(1).WriteSharedStrings(Constants.Values.HeaderColumDescriptionsMin, Constants.Styles.LightBlueBg)
            .BeginRow()
                .SkipColumns(1)
                .Write("FR01")
                .Write("AD")
                .Write(new DateTime(2023, 01, 01), Constants.Styles.ShortDate)
                .Write(new DateTime(2023, 01, 01), Constants.Styles.ShortDate)
                .Write("MERC11-1111444-55500")
                .Write("EUR")
            .SkipRows(1)
            .BeginRow()
                .SkipColumns(1)
                .WriteSharedString(Constants.Values.LineItems, Constants.Styles.BlueBg)
            .BeginRow()
                .SkipColumns(4)
                .WriteSharedString(Constants.Values.TransactionCurrency, Constants.Styles.LightBlueBg, 2)
            .BeginRow()
                .SkipColumns(1)
                .WriteSharedStrings(Constants.Values.LineItemColumnNamesMin, Constants.Styles.LightBlueBg)
            .BeginRow()
                .SkipColumns(1)
                .WriteSharedStrings(Constants.Values.LineItemColumnDescriptionsMin, Constants.Styles.LightBlueBg)
            .BeginRow()
                .SkipColumns(1)
                .Write("FR01")
                .Write("63333333")
                .Write("FNP-MERC-4242424-23232-898989/0101-E3 Meow")
                .Write(35.09)
                .SkipColumns(1)
                .Write("V0")
                .Write("FR01010002_0013")
                .Write("7226")
            .BeginRow()
                .SkipColumns(1)
                .Write("FR01")
                .Write("11100")
                .Write("FNP-MERC-4242424-23232-898989/0101-E3 Meow")
                .Write(7.02)
                .SkipColumns(1)
                .Write("V0")
                .Write("FR01010002_0013")
                .Write("7226")
            .BeginRow()
                .SkipColumns(1)
                .Write("FR01")
                .Write("240205")
                .Write("FNP-MERC-4242424-23232-898989/0101-E3 Meow")
                .SkipColumns(1)
                .Write(42.11)
                .Write("V0")
                .Write("FR01010002_0013")
                .Write("7226")
            .BeginRow();
    }
}

Constants.cs:

using System.Drawing;
using LargeXlsx;

namespace ExcelMeh;

public static class Constants
{
    public static class Colors
    {
        public static readonly Color DarkYellow = Color.FromArgb(255, 192, 0);
        public static readonly Color Blue = Color.FromArgb(180, 198, 231);
        public static readonly Color DarkBlue = Color.FromArgb(142, 169, 219);
        public static readonly Color LightBlue = Color.FromArgb(237, 241, 249);
        public static readonly Color ForestGreen = Color.FromArgb(169, 208, 142);
    }

    public static class Fills
    {
        public static readonly XlsxFill DarkYellow = new(Colors.DarkYellow);
        public static readonly XlsxFill DarkBlue = new(Colors.DarkBlue);
        public static readonly XlsxFill Blue = new(Colors.Blue);
        public static readonly XlsxFill LightBlue = new(Colors.LightBlue);
        public static readonly XlsxFill ForestGreen = new(Colors.ForestGreen);
    }

    public static class Alignments
    {
        public static readonly XlsxAlignment Left = new(XlsxAlignment.Horizontal.Left);
        public static readonly XlsxAlignment Center = new(XlsxAlignment.Horizontal.Center);
        public static readonly XlsxAlignment Right = new(XlsxAlignment.Horizontal.Right);
    }

    public static class Fonts
    {
        public const string Name = "Calibri";
        public const double Size = 10;
        public const double TitleSize = 13;
        public const double BoldSize = 11;

        public static readonly XlsxFont Default = new(Name, Size, Color.Empty);
        public static readonly XlsxFont Title = Default.WithSize(TitleSize);
        public static readonly XlsxFont Bold = Default.WithSize(BoldSize).WithBold();
    }

    public static class NumberFormats
    {
        public const string ShortDatePattern = "DD/MM/YYYY";
        public static readonly XlsxNumberFormat ShortDate = new(ShortDatePattern);
    }

    public static class Styles
    {
        public static readonly XlsxStyle Default =
            XlsxStyle.Default.With(Fonts.Default);

        public static readonly XlsxStyle Title =
            XlsxStyle.Default.With(Fonts.Title);

        public static readonly XlsxStyle Bold =
            XlsxStyle.Default.With(Fonts.Bold);

        public static readonly XlsxStyle DarkYellowBg =
            XlsxStyle.Default.With(Fills.DarkYellow);

        public static readonly XlsxStyle DarkBlueBoldBg =
            Bold.With(Fills.DarkBlue).With(Alignments.Right);
        public static readonly XlsxStyle DarkBlueBoldBgLeft =
            Bold.With(Fills.DarkBlue).With(Alignments.Left);

        public static readonly XlsxStyle BlueBg =
            Default.With(Fills.Blue).With(Alignments.Left);

        public static readonly XlsxStyle LightBlueBg =
            Default.With(Fills.LightBlue).With(Alignments.Center);


        public static readonly XlsxStyle LightGreenBg =
            Default.With(Fills.ForestGreen).With(Alignments.Center);

        public static readonly XlsxStyle ShortDate =
            Default.With(NumberFormats.ShortDate);
    }

    private const double InToPxRatio = 13;

    public static readonly IReadOnlyCollection<XlsxColumn> ColumnsMin = new[]
    {
        XlsxColumn.Formatted(0.54 * InToPxRatio),
        XlsxColumn.Formatted(1.39 * InToPxRatio),
        XlsxColumn.Formatted(1.54 * InToPxRatio),
        XlsxColumn.Formatted(3.53 * InToPxRatio),
        XlsxColumn.Formatted(1.12 * InToPxRatio),
        XlsxColumn.Formatted(1.80 * InToPxRatio),
        XlsxColumn.Formatted(1.80 * InToPxRatio),
        XlsxColumn.Formatted(1.42 * InToPxRatio),
        XlsxColumn.Formatted(1.93 * InToPxRatio)
    };

    public static readonly IReadOnlyCollection<XlsxColumn> Columns = new[]
    {
        XlsxColumn.Formatted(0.54 * InToPxRatio),
        XlsxColumn.Formatted(1.39 * InToPxRatio),
        XlsxColumn.Formatted(1.85 * InToPxRatio),
        XlsxColumn.Formatted(3.86 * InToPxRatio),
        XlsxColumn.Formatted(1.62 * InToPxRatio),
        XlsxColumn.Formatted(1.39 * InToPxRatio),
        XlsxColumn.Formatted(2.47 * InToPxRatio),
        XlsxColumn.Formatted(2.93 * InToPxRatio),
        XlsxColumn.Formatted(1.31 * InToPxRatio),
        XlsxColumn.Formatted(1.70 * InToPxRatio),
        XlsxColumn.Formatted(2.01 * InToPxRatio),
        XlsxColumn.Formatted(2.39 * InToPxRatio),
        XlsxColumn.Formatted(2.01 * InToPxRatio),
        XlsxColumn.Formatted(1.62 * InToPxRatio),
        XlsxColumn.Formatted(2.47 * InToPxRatio),
        XlsxColumn.Formatted(1.16 * InToPxRatio),
        XlsxColumn.Formatted(1.78 * InToPxRatio),
        XlsxColumn.Formatted(1.78 * InToPxRatio),
        XlsxColumn.Formatted(1.54 * InToPxRatio),
        XlsxColumn.Formatted(2.85 * InToPxRatio),
        XlsxColumn.Formatted(1.08 * InToPxRatio),
        XlsxColumn.Formatted(1.54 * InToPxRatio),
        XlsxColumn.Formatted(1.47 * InToPxRatio),
        XlsxColumn.Formatted(1.62 * InToPxRatio),
        XlsxColumn.Formatted(1.93 * InToPxRatio),
        XlsxColumn.Formatted(1.62 * InToPxRatio)
    };

    public static class Values
    {
        public const string WorksheetName = "General Journal Entry";
        public const string Title = "Upload General Journal Entry";
        public const string BatchId = "Batch ID";
        public const string Header = "Header";
        public const string LineItems = "Line Items";
        public const string TransactionCurrency = "Transaction Currency";

        public static readonly IReadOnlyCollection<string> Comments = new[]
        {
            "// To add field columns to the template, please add technical names.",
            "// For a complete list of field columns and their technical names, choose ? in the right upper corner of the app screen and then view the Browse entry of web assistance.",
            "// If you want to add more Profitability Segment (CO-PA) fields, make sure that you add the 'PROF_' prefix in the technical names of the CO-PA fields."
        };

        public static readonly IReadOnlyCollection<string> HeaderColumNamesMin = new[]
        {
            "BUKRS",
            "BLART",
            "BLDAT",
            "BUDAT",
            "BKTXT",
            "WAERS"
        };

        public static readonly IReadOnlyCollection<string> HeaderColumDescriptionsMin = new[]
        {
            "*Company Code (4)",
            "*Journal Entry Type (2)",
            "*Journal Entry Date",
            "*Posting Date",
            "Document Header Text (25)",
            "*Transaction Currency (5)"
        };

        public static readonly IReadOnlyCollection<string> LineItemColumnNamesMin = new[]
        {
            "BUKRS",
            "HKONT",
            "SGTXT",
            "WRSOL",
            "WRHAB",
            "MWSKZ",
            "PS_POSID",
            "YY1_SUPPLIER_ACCRUAL_COB"
        };

        public static readonly IReadOnlyCollection<string> LineItemColumnDescriptionsMin = new[]
        {
            "Company Code (4)",
            "G/L Account (10)",
            "Item Text (50)",
            "Debit",
            "Credit",
            "Tax Code (2)",
            "WBS Element (24)",
            "Supplier Accrual (10)"
        };
    }
}

XlsxWriterExtensions:

using LargeXlsx;

namespace ExcelMeh;

public static class XlsxWriterExtensions
{
    public static XlsxWriter WriteSharedStrings(this XlsxWriter source, IEnumerable<string> values, XlsxStyle style)
    {
        foreach (var value in values)
        {
            source.WriteSharedString(value, style);
        }

        return source;
    }

    public static XlsxWriter BeginRows(this XlsxWriter source, IEnumerable<string> values, XlsxStyle style)
    {
        foreach (var value in values)
        {
            source.BeginRow().Write(value, style);
        }

        return source;
    }
}

Process.cs:

using LargeXlsx;

namespace ExcelMeh;

public static class XlsxWriterExtensions
{
    public static XlsxWriter WriteSharedStrings(this XlsxWriter source, IEnumerable<string> values, XlsxStyle style)
    {
        foreach (var value in values)
        {
            source.WriteSharedString(value, style);
        }

        return source;
    }

Program.cs:

using ExcelMeh;

const string path = @"C:\Users\natalie-perret\Desktop\test.xlsx";
AccrualsGenerator.Generate(path);
DefaultApp.StartDefault(path);

If I'm trying to use the file as-is after they have been generated to push them to SAP, it doesn't work:

image

But if I open them with Libre Office or MS Office and save them, I can then push to SAP:

image

image

File Examples:

Expand / Collapse to see the substantial changes found between the two files / archives (since `*.xslx` are `*.zip` containing `*.xml`'s in disguise)

test - original\xl\styles.xml:

<?xml version="1.0" encoding="utf-8"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <numFmts count="1">
        <numFmt numFmtId="164" formatCode="DD/MM/YYYY"/>
    </numFmts>
    <fonts count="4">
        <font>
            <sz val="11"/>
            <color rgb="000000"/>
            <name val="Calibri"/>
            <family val="2"/>
        </font>
        <font>
            <sz val="13"/>
            <color rgb="000000"/>
            <name val="Calibri"/>
            <family val="2"/>
        </font>
        <font>
            <sz val="11"/>
            <color rgb="000000"/>
            <name val="Calibri"/>
            <family val="2"/>
            <b/>
        </font>
        <font>
            <sz val="10"/>
            <color rgb="000000"/>
            <name val="Calibri"/>
            <family val="2"/>
        </font>
    </fonts>
    <fills count="6">
        <fill>
            <patternFill patternType="none">
                <fgColor rgb="ffffff"/>
                <bgColor rgb="ffffff"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="gray125">
                <fgColor rgb="ffffff"/>
                <bgColor rgb="ffffff"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="ffc000"/>
                <bgColor rgb="ffc000"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="8ea9db"/>
                <bgColor rgb="8ea9db"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="edf1f9"/>
                <bgColor rgb="edf1f9"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="b4c6e7"/>
                <bgColor rgb="b4c6e7"/>
            </patternFill>
        </fill>
    </fills>
    <borders count="1">
        <border diagonalDown="0" diagonalUp="0">
            <left/>
            <right/>
            <top/>
            <bottom/>
            <diagonal/>
        </border>
    </borders>
    <cellXfs count="10">
        <xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyNumberFormat="1" applyFont="1" applyFill="1" applyBorder="1"/>
        <xf numFmtId="0" fontId="1" fillId="0" borderId="0" applyNumberFormat="1" applyFont="1" applyFill="1" applyBorder="1"/>
        <xf numFmtId="0" fontId="2" fillId="0" borderId="0" applyNumberFormat="1" applyFont="1" applyFill="1" applyBorder="1"/>
        <xf numFmtId="0" fontId="0" fillId="2" borderId="0" applyNumberFormat="1" applyFont="1" applyFill="1" applyBorder="1"/>
        <xf numFmtId="0" fontId="2" fillId="3" borderId="0" applyNumberFormat="1" applyFont="1" applyFill="1" applyBorder="1" applyAlignment="1">
            <alignment horizontal="left"/>
        </xf>
        <xf numFmtId="0" fontId="2" fillId="3" borderId="0" applyNumberFormat="1" applyFont="1" applyFill="1" applyBorder="1" applyAlignment="1">
            <alignment horizontal="right"/>
        </xf>
        <xf numFmtId="0" fontId="3" fillId="4" borderId="0" applyNumberFormat="1" applyFont="1" applyFill="1" applyBorder="1" applyAlignment="1">
            <alignment horizontal="center"/>
        </xf>
        <xf numFmtId="0" fontId="3" fillId="0" borderId="0" applyNumberFormat="1" applyFont="1" applyFill="1" applyBorder="1"/>
        <xf numFmtId="164" fontId="3" fillId="0" borderId="0" applyNumberFormat="1" applyFont="1" applyFill="1" applyBorder="1"/>
        <xf numFmtId="0" fontId="3" fillId="5" borderId="0" applyNumberFormat="1" applyFont="1" applyFill="1" applyBorder="1" applyAlignment="1">
            <alignment horizontal="left"/>
        </xf>
    </cellXfs>
</styleSheet>

test - original\xl\worksheets\sheet1.xml:

<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <sheetViews>
        <sheetView workbookViewId="0" rightToLeft="0">
        </sheetView>
    </sheetViews>
    <cols>
        <col min="1" max="1" width="7.0200000000000005" customWidth="1"/>
        <col min="2" max="2" width="18.07" customWidth="1"/>
        <col min="3" max="3" width="20.02" customWidth="1"/>
        <col min="4" max="4" width="45.89" customWidth="1"/>
        <col min="5" max="5" width="14.560000000000002" customWidth="1"/>
        <col min="6" max="6" width="23.400000000000002" customWidth="1"/>
        <col min="7" max="7" width="23.400000000000002" customWidth="1"/>
        <col min="8" max="8" width="18.46" customWidth="1"/>
        <col min="9" max="9" width="25.09" customWidth="1"/>
    </cols>
    <sheetData>
        <row r="1">
            <c r="A1" s="1" t="inlineStr">
                <is>
                    <t>Upload General Journal Entry</t>
                </is>
            </c>
        </row>
        <row r="2">
            <c r="A2" s="2" t="inlineStr">
                <is>
                    <t>// To add field columns to the template, please add technical names.</t>
                </is>
            </c>
        </row>
        <row r="3">
            <c r="A3" s="2" t="inlineStr">
                <is>
                    <t>// For a complete list of field columns and their technical names, choose ? in the right upper corner of the app screen and then view the Browse entry of web assistance.</t>
                </is>
            </c>
        </row>
        <row r="4">
            <c r="A4" s="2" t="inlineStr">
                <is>
                    <t>// If you want to add more Profitability Segment (CO-PA) fields, make sure that you add the 'PROF_' prefix in the technical names of the CO-PA fields.</t>
                </is>
            </c>
        </row>
        <row r="5">
            <c r="A5" s="3" t="inlineStr">
                <is>
                    <t>Batch ID</t>
                </is>
            </c>
            <c r="B5" s="3"/>
        </row>
        <row r="8" s="4" customFormat="1">
            <c r="A8" s="5">
                <v>1</v>
            </c>
            <c r="B8" s="5" t="s">
                <v>0</v>
            </c>
        </row>
        <row r="9">
            <c r="B9" s="6" t="s">
                <v>1</v>
            </c>
            <c r="C9" s="6" t="s">
                <v>2</v>
            </c>
            <c r="D9" s="6" t="s">
                <v>3</v>
            </c>
            <c r="E9" s="6" t="s">
                <v>4</v>
            </c>
            <c r="F9" s="6" t="s">
                <v>5</v>
            </c>
            <c r="G9" s="6" t="s">
                <v>6</v>
            </c>
        </row>
        <row r="10">
            <c r="B10" s="6" t="s">
                <v>7</v>
            </c>
            <c r="C10" s="6" t="s">
                <v>8</v>
            </c>
            <c r="D10" s="6" t="s">
                <v>9</v>
            </c>
            <c r="E10" s="6" t="s">
                <v>10</v>
            </c>
            <c r="F10" s="6" t="s">
                <v>11</v>
            </c>
            <c r="G10" s="6" t="s">
                <v>12</v>
            </c>
        </row>
        <row r="11">
            <c r="B11" s="7" t="inlineStr">
                <is>
                    <t>FR01</t>
                </is>
            </c>
            <c r="C11" s="7" t="inlineStr">
                <is>
                    <t>AD</t>
                </is>
            </c>
            <c r="D11" s="8">
                <v>44927</v>
            </c>
            <c r="E11" s="8">
                <v>44927</v>
            </c>
            <c r="F11" s="7" t="inlineStr">
                <is>
                    <t>MERC11-1111444-55500</t>
                </is>
            </c>
            <c r="G11" s="7" t="inlineStr">
                <is>
                    <t>EUR</t>
                </is>
            </c>
        </row>
        <row r="13">
            <c r="B13" s="9" t="s">
                <v>13</v>
            </c>
        </row>
        <row r="14">
            <c r="E14" s="6" t="s">
                <v>14</v>
            </c>
            <c r="F14" s="6"/>
        </row>
        <row r="15">
            <c r="B15" s="6" t="s">
                <v>1</v>
            </c>
            <c r="C15" s="6" t="s">
                <v>15</v>
            </c>
            <c r="D15" s="6" t="s">
                <v>16</v>
            </c>
            <c r="E15" s="6" t="s">
                <v>17</v>
            </c>
            <c r="F15" s="6" t="s">
                <v>18</v>
            </c>
            <c r="G15" s="6" t="s">
                <v>19</v>
            </c>
            <c r="H15" s="6" t="s">
                <v>20</v>
            </c>
            <c r="I15" s="6" t="s">
                <v>21</v>
            </c>
        </row>
        <row r="16">
            <c r="B16" s="6" t="s">
                <v>22</v>
            </c>
            <c r="C16" s="6" t="s">
                <v>23</v>
            </c>
            <c r="D16" s="6" t="s">
                <v>24</v>
            </c>
            <c r="E16" s="6" t="s">
                <v>25</v>
            </c>
            <c r="F16" s="6" t="s">
                <v>26</v>
            </c>
            <c r="G16" s="6" t="s">
                <v>27</v>
            </c>
            <c r="H16" s="6" t="s">
                <v>28</v>
            </c>
            <c r="I16" s="6" t="s">
                <v>29</v>
            </c>
        </row>
        <row r="17">
            <c r="B17" s="7" t="inlineStr">
                <is>
                    <t>FR01</t>
                </is>
            </c>
            <c r="C17" s="7" t="inlineStr">
                <is>
                    <t>63333333</t>
                </is>
            </c>
            <c r="D17" s="7" t="inlineStr">
                <is>
                    <t>FNP-MERC-4242424-23232-898989/0101-E3 Meow</t>
                </is>
            </c>
            <c r="E17" s="7">
                <v>35.09</v>
            </c>
            <c r="G17" s="7" t="inlineStr">
                <is>
                    <t>V0</t>
                </is>
            </c>
            <c r="H17" s="7" t="inlineStr">
                <is>
                    <t>FR01010002_0013</t>
                </is>
            </c>
            <c r="I17" s="7" t="inlineStr">
                <is>
                    <t>7226</t>
                </is>
            </c>
        </row>
        <row r="18">
            <c r="B18" s="7" t="inlineStr">
                <is>
                    <t>FR01</t>
                </is>
            </c>
            <c r="C18" s="7" t="inlineStr">
                <is>
                    <t>11100</t>
                </is>
            </c>
            <c r="D18" s="7" t="inlineStr">
                <is>
                    <t>FNP-MERC-4242424-23232-898989/0101-E3 Meow</t>
                </is>
            </c>
            <c r="E18" s="7">
                <v>7.02</v>
            </c>
            <c r="G18" s="7" t="inlineStr">
                <is>
                    <t>V0</t>
                </is>
            </c>
            <c r="H18" s="7" t="inlineStr">
                <is>
                    <t>FR01010002_0013</t>
                </is>
            </c>
            <c r="I18" s="7" t="inlineStr">
                <is>
                    <t>7226</t>
                </is>
            </c>
        </row>
        <row r="19">
            <c r="B19" s="7" t="inlineStr">
                <is>
                    <t>FR01</t>
                </is>
            </c>
            <c r="C19" s="7" t="inlineStr">
                <is>
                    <t>240205</t>
                </is>
            </c>
            <c r="D19" s="7" t="inlineStr">
                <is>
                    <t>FNP-MERC-4242424-23232-898989/0101-E3 Meow</t>
                </is>
            </c>
            <c r="F19" s="7">
                <v>42.11</v>
            </c>
            <c r="G19" s="7" t="inlineStr">
                <is>
                    <t>V0</t>
                </is>
            </c>
            <c r="H19" s="7" t="inlineStr">
                <is>
                    <t>FR01010002_0013</t>
                </is>
            </c>
            <c r="I19" s="7" t="inlineStr">
                <is>
                    <t>7226</t>
                </is>
            </c>
        </row>
        <row r="20">
        </row>
    </sheetData>
    <mergeCells count="1">
        <mergeCell ref="E14:F14"/>
    </mergeCells>
</worksheet>

test - saved with editor\xl\styles.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <numFmts count="2">
        <numFmt numFmtId="164" formatCode="General"/>
        <numFmt numFmtId="165" formatCode="dd/mm/yyyy"/>
    </numFmts>
    <fonts count="8">
        <font>
            <sz val="10"/>
            <name val="Arial"/>
            <family val="2"/>
        </font>
        <font>
            <sz val="10"/>
            <name val="Arial"/>
            <family val="0"/>
        </font>
        <font>
            <sz val="10"/>
            <name val="Arial"/>
            <family val="0"/>
        </font>
        <font>
            <sz val="10"/>
            <name val="Arial"/>
            <family val="0"/>
        </font>
        <font>
            <sz val="13"/>
            <color rgb="FF000000"/>
            <name val="Calibri"/>
            <family val="2"/>
            <charset val="1"/>
        </font>
        <font>
            <b val="true"/>
            <sz val="11"/>
            <color rgb="FF000000"/>
            <name val="Calibri"/>
            <family val="2"/>
            <charset val="1"/>
        </font>
        <font>
            <sz val="11"/>
            <color rgb="FF000000"/>
            <name val="Calibri"/>
            <family val="2"/>
            <charset val="1"/>
        </font>
        <font>
            <sz val="10"/>
            <color rgb="FF000000"/>
            <name val="Calibri"/>
            <family val="2"/>
            <charset val="1"/>
        </font>
    </fonts>
    <fills count="6">
        <fill>
            <patternFill patternType="none"/>
        </fill>
        <fill>
            <patternFill patternType="gray125"/>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="FFFFC000"/>
                <bgColor rgb="FFFF9900"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="FF8EA9DB"/>
                <bgColor rgb="FF969696"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="FFEDF1F9"/>
                <bgColor rgb="FFFFFFCC"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="FFB4C6E7"/>
                <bgColor rgb="FFCCCCFF"/>
            </patternFill>
        </fill>
    </fills>
    <borders count="1">
        <border diagonalUp="false" diagonalDown="false">
            <left/>
            <right/>
            <top/>
            <bottom/>
            <diagonal/>
        </border>
    </borders>
    <cellStyleXfs count="20">
        <xf numFmtId="164" fontId="0" fillId="0" borderId="0" applyFont="true" applyBorder="true" applyAlignment="true" applyProtection="true">
            <alignment horizontal="general" vertical="bottom" textRotation="0" wrapText="false" indent="0" shrinkToFit="false"/>
            <protection locked="true" hidden="false"/>
        </xf>
        <xf numFmtId="0" fontId="1" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="0" fontId="1" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="0" fontId="2" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="0" fontId="2" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="43" fontId="1" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="41" fontId="1" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="44" fontId="1" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="42" fontId="1" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
        <xf numFmtId="9" fontId="1" fillId="0" borderId="0" applyFont="true" applyBorder="false" applyAlignment="false" applyProtection="false"></xf>
    </cellStyleXfs>
    <cellXfs count="10">
        <xf numFmtId="164" fontId="0" fillId="0" borderId="0" xfId="0" applyFont="false" applyBorder="false" applyAlignment="false" applyProtection="false">
            <alignment horizontal="general" vertical="bottom" textRotation="0" wrapText="false" indent="0" shrinkToFit="false"/>
            <protection locked="true" hidden="false"/>
        </xf>
        <xf numFmtId="164" fontId="4" fillId="0" borderId="0" xfId="0" applyFont="true" applyBorder="true" applyAlignment="false" applyProtection="false">
            <alignment horizontal="general" vertical="bottom" textRotation="0" wrapText="false" indent="0" shrinkToFit="false"/>
            <protection locked="true" hidden="false"/>
        </xf>
        <xf numFmtId="164" fontId="5" fillId="0" borderId="0" xfId="0" applyFont="true" applyBorder="true" applyAlignment="false" applyProtection="false">
            <alignment horizontal="general" vertical="bottom" textRotation="0" wrapText="false" indent="0" shrinkToFit="false"/>
            <protection locked="true" hidden="false"/>
        </xf>
        <xf numFmtId="164" fontId="6" fillId="2" borderId="0" xfId="0" applyFont="true" applyBorder="true" applyAlignment="false" applyProtection="false">
            <alignment horizontal="general" vertical="bottom" textRotation="0" wrapText="false" indent="0" shrinkToFit="false"/>
            <protection locked="true" hidden="false"/>
        </xf>
        <xf numFmtId="164" fontId="5" fillId="3" borderId="0" xfId="0" applyFont="true" applyBorder="true" applyAlignment="true" applyProtection="false">
            <alignment horizontal="right" vertical="bottom" textRotation="0" wrapText="false" indent="0" shrinkToFit="false"/>
            <protection locked="true" hidden="false"/>
        </xf>
        <xf numFmtId="164" fontId="5" fillId="3" borderId="0" xfId="0" applyFont="true" applyBorder="true" applyAlignment="true" applyProtection="false">
            <alignment horizontal="left" vertical="bottom" textRotation="0" wrapText="false" indent="0" shrinkToFit="false"/>
            <protection locked="true" hidden="false"/>
        </xf>
        <xf numFmtId="164" fontId="7" fillId="4" borderId="0" xfId="0" applyFont="true" applyBorder="true" applyAlignment="true" applyProtection="false">
            <alignment horizontal="center" vertical="bottom" textRotation="0" wrapText="false" indent="0" shrinkToFit="false"/>
            <protection locked="true" hidden="false"/>
        </xf>
        <xf numFmtId="164" fontId="7" fillId="0" borderId="0" xfId="0" applyFont="true" applyBorder="true" applyAlignment="false" applyProtection="false">
            <alignment horizontal="general" vertical="bottom" textRotation="0" wrapText="false" indent="0" shrinkToFit="false"/>
            <protection locked="true" hidden="false"/>
        </xf>
        <xf numFmtId="165" fontId="7" fillId="0" borderId="0" xfId="0" applyFont="true" applyBorder="true" applyAlignment="false" applyProtection="false">
            <alignment horizontal="general" vertical="bottom" textRotation="0" wrapText="false" indent="0" shrinkToFit="false"/>
            <protection locked="true" hidden="false"/>
        </xf>
        <xf numFmtId="164" fontId="7" fillId="5" borderId="0" xfId="0" applyFont="true" applyBorder="true" applyAlignment="true" applyProtection="false">
            <alignment horizontal="left" vertical="bottom" textRotation="0" wrapText="false" indent="0" shrinkToFit="false"/>
            <protection locked="true" hidden="false"/>
        </xf>
    </cellXfs>
    <cellStyles count="6">
        <cellStyle name="Normal" xfId="0" builtinId="0"/>
        <cellStyle name="Comma" xfId="15" builtinId="3"/>
        <cellStyle name="Comma [0]" xfId="16" builtinId="6"/>
        <cellStyle name="Currency" xfId="17" builtinId="4"/>
        <cellStyle name="Currency [0]" xfId="18" builtinId="7"/>
        <cellStyle name="Percent" xfId="19" builtinId="5"/>
    </cellStyles>
    <colors>
        <indexedColors>
            <rgbColor rgb="FF000000"/>
            <rgbColor rgb="FFEDF1F9"/>
            <rgbColor rgb="FFFF0000"/>
            <rgbColor rgb="FF00FF00"/>
            <rgbColor rgb="FF0000FF"/>
            <rgbColor rgb="FFFFFF00"/>
            <rgbColor rgb="FFFF00FF"/>
            <rgbColor rgb="FF00FFFF"/>
            <rgbColor rgb="FF800000"/>
            <rgbColor rgb="FF008000"/>
            <rgbColor rgb="FF000080"/>
            <rgbColor rgb="FF808000"/>
            <rgbColor rgb="FF800080"/>
            <rgbColor rgb="FF008080"/>
            <rgbColor rgb="FFB4C6E7"/>
            <rgbColor rgb="FF808080"/>
            <rgbColor rgb="FF8EA9DB"/>
            <rgbColor rgb="FF993366"/>
            <rgbColor rgb="FFFFFFCC"/>
            <rgbColor rgb="FFCCFFFF"/>
            <rgbColor rgb="FF660066"/>
            <rgbColor rgb="FFFF8080"/>
            <rgbColor rgb="FF0066CC"/>
            <rgbColor rgb="FFCCCCFF"/>
            <rgbColor rgb="FF000080"/>
            <rgbColor rgb="FFFF00FF"/>
            <rgbColor rgb="FFFFFF00"/>
            <rgbColor rgb="FF00FFFF"/>
            <rgbColor rgb="FF800080"/>
            <rgbColor rgb="FF800000"/>
            <rgbColor rgb="FF008080"/>
            <rgbColor rgb="FF0000FF"/>
            <rgbColor rgb="FF00CCFF"/>
            <rgbColor rgb="FFCCFFFF"/>
            <rgbColor rgb="FFCCFFCC"/>
            <rgbColor rgb="FFFFFF99"/>
            <rgbColor rgb="FF99CCFF"/>
            <rgbColor rgb="FFFF99CC"/>
            <rgbColor rgb="FFCC99FF"/>
            <rgbColor rgb="FFFFCC99"/>
            <rgbColor rgb="FF3366FF"/>
            <rgbColor rgb="FF33CCCC"/>
            <rgbColor rgb="FF99CC00"/>
            <rgbColor rgb="FFFFC000"/>
            <rgbColor rgb="FFFF9900"/>
            <rgbColor rgb="FFFF6600"/>
            <rgbColor rgb="FF666699"/>
            <rgbColor rgb="FF969696"/>
            <rgbColor rgb="FF003366"/>
            <rgbColor rgb="FF339966"/>
            <rgbColor rgb="FF003300"/>
            <rgbColor rgb="FF333300"/>
            <rgbColor rgb="FF993300"/>
            <rgbColor rgb="FF993366"/>
            <rgbColor rgb="FF333399"/>
            <rgbColor rgb="FF333333"/>
        </indexedColors>
    </colors>
</styleSheet>

test - saved with editor\xl\worksheets\sheet1.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
    xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
    xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
    xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
    <sheetPr filterMode="false">
        <pageSetUpPr fitToPage="false"/>
    </sheetPr>
    <dimension ref="A1:I19"/>
    <sheetViews>
        <sheetView showFormulas="false" showGridLines="true" showRowColHeaders="true" showZeros="true" rightToLeft="false" tabSelected="true" showOutlineSymbols="true" defaultGridColor="true" view="normal" topLeftCell="A1" colorId="64" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100" workbookViewId="0">
            <selection pane="topLeft" activeCell="A1" activeCellId="0" sqref="A1"/>
        </sheetView>
    </sheetViews>
    <sheetFormatPr defaultColWidth="8.515625" defaultRowHeight="12.8" zeroHeight="false" outlineLevelRow="0" outlineLevelCol="0"></sheetFormatPr>
    <cols>
        <col collapsed="false" customWidth="true" hidden="false" outlineLevel="0" max="1" min="1" style="0" width="7.02"/>
        <col collapsed="false" customWidth="true" hidden="false" outlineLevel="0" max="2" min="2" style="0" width="18.07"/>
        <col collapsed="false" customWidth="true" hidden="false" outlineLevel="0" max="3" min="3" style="0" width="20.03"/>
        <col collapsed="false" customWidth="true" hidden="false" outlineLevel="0" max="4" min="4" style="0" width="45.89"/>
        <col collapsed="false" customWidth="true" hidden="false" outlineLevel="0" max="5" min="5" style="0" width="14.56"/>
        <col collapsed="false" customWidth="true" hidden="false" outlineLevel="0" max="7" min="6" style="0" width="23.4"/>
        <col collapsed="false" customWidth="true" hidden="false" outlineLevel="0" max="8" min="8" style="0" width="18.46"/>
        <col collapsed="false" customWidth="true" hidden="false" outlineLevel="0" max="9" min="9" style="0" width="25.09"/>
    </cols>
    <sheetData>
        <row r="1" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="A1" s="1" t="s">
                <v>0</v>
            </c>
        </row>
        <row r="2" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="A2" s="2" t="s">
                <v>1</v>
            </c>
        </row>
        <row r="3" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="A3" s="2" t="s">
                <v>2</v>
            </c>
        </row>
        <row r="4" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="A4" s="2" t="s">
                <v>3</v>
            </c>
        </row>
        <row r="5" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="A5" s="3" t="s">
                <v>4</v>
            </c>
            <c r="B5" s="3"/>
        </row>
        <row r="8" s="5" customFormat="true" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="A8" s="4" t="n">
                <v>1</v>
            </c>
            <c r="B8" s="4" t="s">
                <v>5</v>
            </c>
        </row>
        <row r="9" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="B9" s="6" t="s">
                <v>6</v>
            </c>
            <c r="C9" s="6" t="s">
                <v>7</v>
            </c>
            <c r="D9" s="6" t="s">
                <v>8</v>
            </c>
            <c r="E9" s="6" t="s">
                <v>9</v>
            </c>
            <c r="F9" s="6" t="s">
                <v>10</v>
            </c>
            <c r="G9" s="6" t="s">
                <v>11</v>
            </c>
        </row>
        <row r="10" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="B10" s="6" t="s">
                <v>12</v>
            </c>
            <c r="C10" s="6" t="s">
                <v>13</v>
            </c>
            <c r="D10" s="6" t="s">
                <v>14</v>
            </c>
            <c r="E10" s="6" t="s">
                <v>15</v>
            </c>
            <c r="F10" s="6" t="s">
                <v>16</v>
            </c>
            <c r="G10" s="6" t="s">
                <v>17</v>
            </c>
        </row>
        <row r="11" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="B11" s="7" t="s">
                <v>18</v>
            </c>
            <c r="C11" s="7" t="s">
                <v>19</v>
            </c>
            <c r="D11" s="8" t="n">
                <v>44927</v>
            </c>
            <c r="E11" s="8" t="n">
                <v>44927</v>
            </c>
            <c r="F11" s="7" t="s">
                <v>20</v>
            </c>
            <c r="G11" s="7" t="s">
                <v>21</v>
            </c>
        </row>
        <row r="13" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="B13" s="9" t="s">
                <v>22</v>
            </c>
        </row>
        <row r="14" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="E14" s="6" t="s">
                <v>23</v>
            </c>
            <c r="F14" s="6"/>
        </row>
        <row r="15" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="B15" s="6" t="s">
                <v>6</v>
            </c>
            <c r="C15" s="6" t="s">
                <v>24</v>
            </c>
            <c r="D15" s="6" t="s">
                <v>25</v>
            </c>
            <c r="E15" s="6" t="s">
                <v>26</v>
            </c>
            <c r="F15" s="6" t="s">
                <v>27</v>
            </c>
            <c r="G15" s="6" t="s">
                <v>28</v>
            </c>
            <c r="H15" s="6" t="s">
                <v>29</v>
            </c>
            <c r="I15" s="6" t="s">
                <v>30</v>
            </c>
        </row>
        <row r="16" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="B16" s="6" t="s">
                <v>31</v>
            </c>
            <c r="C16" s="6" t="s">
                <v>32</v>
            </c>
            <c r="D16" s="6" t="s">
                <v>33</v>
            </c>
            <c r="E16" s="6" t="s">
                <v>34</v>
            </c>
            <c r="F16" s="6" t="s">
                <v>35</v>
            </c>
            <c r="G16" s="6" t="s">
                <v>36</v>
            </c>
            <c r="H16" s="6" t="s">
                <v>37</v>
            </c>
            <c r="I16" s="6" t="s">
                <v>38</v>
            </c>
        </row>
        <row r="17" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="B17" s="7" t="s">
                <v>18</v>
            </c>
            <c r="C17" s="7" t="s">
                <v>39</v>
            </c>
            <c r="D17" s="7" t="s">
                <v>40</v>
            </c>
            <c r="E17" s="7" t="n">
                <v>35.09</v>
            </c>
            <c r="G17" s="7" t="s">
                <v>41</v>
            </c>
            <c r="H17" s="7" t="s">
                <v>42</v>
            </c>
            <c r="I17" s="7" t="s">
                <v>43</v>
            </c>
        </row>
        <row r="18" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="B18" s="7" t="s">
                <v>18</v>
            </c>
            <c r="C18" s="7" t="s">
                <v>44</v>
            </c>
            <c r="D18" s="7" t="s">
                <v>40</v>
            </c>
            <c r="E18" s="7" t="n">
                <v>7.02</v>
            </c>
            <c r="G18" s="7" t="s">
                <v>41</v>
            </c>
            <c r="H18" s="7" t="s">
                <v>42</v>
            </c>
            <c r="I18" s="7" t="s">
                <v>43</v>
            </c>
        </row>
        <row r="19" customFormat="false" ht="12.8" hidden="false" customHeight="false" outlineLevel="0" collapsed="false">
            <c r="B19" s="7" t="s">
                <v>18</v>
            </c>
            <c r="C19" s="7" t="s">
                <v>45</v>
            </c>
            <c r="D19" s="7" t="s">
                <v>40</v>
            </c>
            <c r="F19" s="7" t="n">
                <v>42.11</v>
            </c>
            <c r="G19" s="7" t="s">
                <v>41</v>
            </c>
            <c r="H19" s="7" t="s">
                <v>42</v>
            </c>
            <c r="I19" s="7" t="s">
                <v>43</v>
            </c>
        </row>
    </sheetData>
    <mergeCells count="1">
        <mergeCell ref="E14:F14"/>
    </mergeCells>
    <printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"/>
    <pageMargins left="0.747916666666667" right="0.747916666666667" top="0.984027777777778" bottom="0.984027777777778" header="0.511811023622047" footer="0.511811023622047"/>
    <pageSetup paperSize="1" scale="100" fitToWidth="1" fitToHeight="1" pageOrder="downThenOver" orientation="portrait" blackAndWhite="false" draft="false" cellComments="none" horizontalDpi="300" verticalDpi="300" copies="1"/>
    <headerFooter differentFirst="false" differentOddEven="false">
        <oddHeader></oddHeader>
        <oddFooter></oddFooter>
    </headerFooter>
</worksheet>

I was wondering if you had any idea about the difference provided when Libre Office has saved the original file to copy ๐Ÿค”.
I mean there are changes, but it's still "kinda" the same thing but different enough that SAP can accept the file ๐Ÿค”

Would like to avoid resaving the file via Libre / MS Office to be able to push those files to SAP.

Any idea / hint?

Since version 1.7.1 the FirstRowNum property is -1

Hi,

I have a Unit Test which checks the generated output of a method which uses your library. To check the output, I use NPOI to read the output and the first row to check some header values. Since the version 1.7.1 the test failed with a NullReference, because i access the first row by using sheet.GetRow(0). By further analyzing i came across the FirstRowNum property which is -1.

Here is an example:

using var stream = new MemoryStream();
using (var xlsxWriter = new XlsxWriter(stream))
{
	xlsxWriter
		.BeginWorksheet("Sheet 1")
		.BeginRow().Write("Name").Write("Location").Write("Height (m)")
		.BeginRow().Write("Kingda Ka").Write("Six Flags Great Adventure").Write(139)
		.BeginRow().Write("Top Thrill Dragster").Write("Cedar Point").Write(130)
		.BeginRow().Write("Superman: Escape from Krypton").Write("Six Flags Magic Mountain").Write(126);
}

stream.Seek(0, SeekOrigin.Begin);

IWorkbook workbook;
using (var file = new MemoryStream(stream.ToArray()))
{
	workbook = new XSSFWorkbook(file);
}
Console.WriteLine(workbook.GetSheet("Sheet 1").FirstRowNum);

With the above example using LargeXlsx version 1.7.0 FirstRowNum is 0, with version 1.7.1 FirstRowNum is -1.

It need to have something to do with the line breaks which you added, but I haven't further analyzed it. Can you please have a look on it?

Hide Worksheet

hi.
I would like to get functionality for hiding worksheet in Excel

Writing multi line cell content

Hi, thanks for the great library.

How to properly write a cell with multi-line string content? I have tried using \n and \r\n in the string, but when opened in Excel, it is shown as single line cell (without the \n or \r\n). The weird part is, the cell become multi-line only after I double-clicked (or editing) that cell in Excel.

Create File using MemoryStream

Hi,

i'm trying to create a huge xlsx (308K rows) with your library; all of your documentation use FileStream but for our case we need to use a memorystream to save the output to our MongoGridFs

            using var stream = new MemoryStream();
            {
                using var xlsxWriter = new LargeXlsx.XlsxWriter(stream);
                {
                            CreateLargeSheetByType(xlsxWriter, type, data);
                }
                file = new OutputFileItem
                {
                    FileName = $"{fileNamePrefix} - {type.Description()}.xlsx",
                    Stream = stream.ToArray()
                };
                return _filePersistorService.StoreOutputFile(file);
            }

this code create the file but excel report that format or extension are not valid

Am I missing something? It is possible to use your library this way

thanks
Maurizio

Async API introduction

Hi ! I have a question regarding the implementation of the library: will there be any plans for async API for Excel writer? This may seem exotic but it's actually very useful: let's say you want to write an excel file to some external location (for example, AWS S3). You have implemented a new AmazonS3Stream (derived from Stream) for that purpose which sends some requests from time to time using AWS API. Those requests are asynchronous which means that whole new AmazonS3Stream is asynchronous as well. But async Stream API is not supported by the library, so you will need to put GetAwaiter().GetResult() everywhere, which is not very nice :(

So, what would you think if we introduce that into the library? I can help if help is needed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.