Spredin

File formats

This is the same document you'll find inside Spredin under Help.

Spredin reads and writes several formats. Each one carries a different amount of your work. This page is the plain-language map: pick .sprd to keep everything, Excel to hand off to Excel/Sheets, CSV/TSV or JSON for raw values.


The one-line summary#

You want to… Use Menu
Keep a perfect copy of your work .sprd / .sprd File → Save
Give it to an Excel / Google Sheets user .xlsx (all sheets) File → Export → Excel
Feed raw values to another tool or an agent .json (all sheets) File → Export → JSON
One flat table of values .csv / .tsv (active sheet) File → Export → CSV / TSV

Golden rule: work in .sprd. It is the only format that preserves everything — and it is open, human-readable JSON you (or an agent) can read, diff, and generate.


Opening an Excel file — what survives, and what does not#

The table above is about saving. This one is about the other direction: you double-click a colleague's workbook and open it here. It is a different question with a different answer, and the honest version is that Spredin brings across your data and its formatting and leaves behind Excel's automation and drawing layers.

✅ kept · 🟡 partial · ❌ dropped

What's in the Excel file .xlsx .xls
Cell values, all sheets
Formulas (values imported instead — see below)
Number formats (dates, currency, %)
Fonts, sizes, bold/italic/underline
Text and fill colours, incl. theme colours
Borders (presence) 🟡 no per-edge style
Alignment, vertical alignment, wrap
Merged cells
Column widths / row heights
Hidden rows / columns
Frozen panes
Cell comments (classic)
Data validation (dropdowns, rules)
Named ranges
Pivot tables ✅ rebuilt as live Spredin pivots
Conditional formatting rules
Charts rebuild here — Spredin charts are Apache ECharts, and they live in .sprd
Images, shapes, text boxes
Macros / VBA replaced by the Python panel — real Python with pandas/numpy, not a macro language
Hyperlinks
Sheet & workbook protection
AutoFilter state, sort state
Strikethrough, text rotation, indent
Patterned fills (hatching, e.g. gray125) solid fills come across; a patterned one arrives unfilled

The short version. Your numbers, formulas and the way the sheet looks come across from .xlsx. Charts and conditional-formatting rules do not — rebuild them here (both are better here, and both live in .sprd). VBA never will; the replacement is the Python panel, which is a real programming language with pandas in it — see Coming from Excel?.

How big a workbook can Spredin open?#

The limit is cell count, not file size, and it applies per sheet: about 12 million cells in any one sheet. A sheet past that is refused by name and the rest of the workbook still opens.

In practice: on a large real workbook — tens of megabytes, a dozen-plus sheets, one of them well past ten million cells — the first sheet is usable in well under a second and the whole book lands in a few seconds. Sheets hydrate in the background, so a large workbook is workable long before it has finished loading — a sheet still arriving says so, and says how big it is.

If a single sheet is refused: open that one on its own. In Excel, save it as .csv or as its own .xlsx, and open that. Everything else in the workbook is unaffected.

Nothing is lost from the original file. Opening never writes to it. If the import drops something you need, the .xlsx on disk is untouched.

.xls carries data only. See the next section for why — it is not an oversight, it is a different file format wearing a similar name.


Opening a .csv or .tsv — what gets converted#

Encoding and separator are detected. A CSV saved by Excel on Windows (in the Windows code page) opens as readily as a UTF-8 one, with or without a byte-order mark; so does a UTF-16 file that starts with one. A file that separates fields with semicolons — what Excel writes in regions whose decimal separator is a comma — is split on the semicolons, and its values are read that region's way: 1.234,56 is 1234.56 and 15.03.2024 is a date. Excel's own sep=; first line is honoured. A .tsv is always tab-separated.

A text file has no types, so the reader has to decide what each field means. Spredin applies exactly the same rule as typing into a cell, so a value behaves the same however it arrived:

In the file Becomes
1,200 · $1,200 · €3.5 · (1,200) · 50% a number with a matching format
2024-01-15 · 1/15/2024 (or 15/01/2024 on a day-first Mac — the system region decides, as in Excel) · 12:30 a date/time serial with a date format
1200 · -3.5 · 1.2E5 a number
007 · 00123 text — leading zeros are kept
a digit string longer than 15 digits text — every digit is kept
SEPT2 · MARCH1 · 1-3 · 3/4 · +1 (555) 010-1234 text, unchanged

The last three rows are where we deliberately differ from Excel, which turns SEPT2 into a date, drops the zeros from 007, and rounds a long account number away into scientific notation. Spredin does not: a value you can see is a value you can compute with.

One consequence worth knowing: because 007 is text, SUM over a column of them is 0 (Excel ignores text inside a range) — while =A1+1 is still 8, since operators coerce number-looking text. That is Excel's own split between operators and aggregation, applied to a value Excel would have converted on the way in.

The Python API is exemptsheet.set(r, c, "007") writes 007 and sheet.set(r, c, "50%") writes the text 50%, because a script has real types available and literalness is the more useful contract there.


.xls vs .xlsx — why the older one loses formatting#

They share a name and nothing else. .xlsx is not ".xls version 2"; it is a complete replacement that happens to serve the same purpose.

.xls (Excel 97–2003) .xlsx (Excel 2007+)
What it actually is a binary OLE2 compound file — a little filesystem of byte records a ZIP of XML documents (rename one to .zip and look inside)
Standard Microsoft's [MS-XLS] binary spec ECMA-376 / ISO 29500, an open standard
Max sheet size 65,536 rows × 256 columns 1,048,576 rows × 16,384 columns
Typical size larger smaller (it is compressed)
Corruption recovery poor — one bad region can take the file good — parts are independent

Because the containers have nothing in common, the high-fidelity reader that pulls styles, merges, freezes and comments out of the .xlsx XML cannot be pointed at a .xls — there is no XML in there to read. Spredin decodes .xls records for values and formulas, and stops there.

Reading and writing .xls#

What to do instead: open the .xls, work, then File → Export → Excel to get .xlsx. Excel 2007 and later open it natively; Excel 2003 opens it with Microsoft's free Compatibility Pack. If your recipient truly cannot take .xlsx, export .csv — every version of Excel ever shipped reads that.

Practical note: opening a .xls and exporting .xlsx is a one-way upgrade of the container. The formatting the .xls had was already dropped on import, so the new file has your data and Spredin's formatting, not the original's.


What each format preserves when Spredin saves or exports#

✅ kept · 🟡 partial · ❌ dropped

Your content .sprd .xlsx .json .csv/.tsv
Cell values
Formulas (=SUM(…)) ❌ values only ❌ values only
Number formats, fonts, colours, fills, borders, alignment, wrap
Merged cells
Column widths / row heights
Frozen panes
Cell comments / notes
Data validation (dropdowns / rules)
Named ranges
Conditional formatting ✅ as live Excel rules
AutoFilter (which rows a filter is hiding)
Structured tables (Table1[Amount])
Charts
Python scripts
Multiple sheets ❌ active sheet only

Anything Spredin-specific — charts, the bundled Python — lives only in .sprd. Conditional formatting is written to .xlsx as Excel's own rule types (cell / text / duplicate / top-N / colour scale / data bar), so it keeps responding to the data over there rather than freezing into fixed fills. It is not read back IN from .xlsx yet.


The formats, one by one#

Spredin's native format, and the default when you Save. Nothing is lost. Inside it is plain, human-readable JSON — one compact CSV line per row for the cells, with separate sections for styles, merges, sizes, freeze, conditional formatting, charts, named ranges, the active AutoFilter, structured tables and the Python files.

The file is gzip-compressed (~80% smaller — a 1M-row sheet is ~16 MB, beating the equivalent .xlsx or .csv), but the compression is inside the file rather than in the name: there is one extension to know, and Finder can't mistake a workbook for an archive. .xlsx is secretly a zip and .numbers a package; neither says so either. Spredin reads a .sprd whether or not it is compressed — it checks the file's first two bytes, not its name — so you can gunzip one to read the JSON by hand and it will still open.

Because it's open JSON, an agent or script can read and write it directly. One shape worth knowing: formatting that came from an imported .xlsx is stored the way Excel stores it — a styleTable of the distinct formats plus styleRuns row spans into it — because a real workbook uses a handful of formats across millions of cells, and writing one entry per cell made saving a large import slow enough to look frozen. When styleRuns is present, the styles section holds only the cells you edited yourself, and an empty {} there means "this cell was explicitly cleared". A file with no styleRuns has the whole map in styles, which is how every earlier file reads.

Pre-1.0 files. .spredy and .spredy.gz still open. They were the same format under the old product name.

.xlsx — Excel / Google Sheets interop#

Export → Excel writes every sheet (empty ones too) with values, formulas, and per-cell styling (bold/italic/underline, text + fill colours, alignment, wrap, borders, number format, font). Use it to hand data to someone in Excel or Sheets. It does not carry Spredin charts or the Python scripts. Conditional formatting goes as Excel's own live rules (see the table above).

Spredin reads .xlsx at high fidelity — all sheets with styling, number formats, merges, sizes, frozen panes, comments, data validation and named ranges. Writing back is .xlsx only (the legacy .xls binary format is import-only).

Sheet names. Excel caps a sheet name at 31 characters, forbids : \ / ? * [ ], and will not have two sheets with the same name (it compares them case-insensitively, so Sales and sales clash). Spredin applies those rules wherever a name is chosen — renaming a tab refuses a name Excel would not accept, a script's add_sheet raises an error for one, and importing data[2025].csv names the sheet data 2025 — so an ordinary workbook exports unchanged. Renaming a tab updates every formula and defined name that referred to it, in the same undo step, as Excel does. A workbook saved by an older version can still hold an illegal name; export makes it legal and rewrites the formulas and names that point at it, so the file still adds up.

Identifiers stay text. A cell holding 007, a ZIP code or a 16-digit account number is text in Spredin, and it is written to Excel as text, so no leading zero or trailing digit is lost.

Defined names are written absolute (Data!$B$2). Excel reads a name without $ as relative to the cell using it; Spredin treats names as fixed ranges either way, and the export makes Excel agree.

Very long text. Excel holds at most 32,767 characters in a cell. If a cell holds more, Export → Excel names the cells and asks before writing; the exported copy has that text cut to fit, and your workbook keeps it in full.

.json — plain values out (export only)#

Export → JSON writes every sheet's computed values as { "sheets": [ { "name": "Sheet1", "rows": [[…], …] } ] }. Formulas are evaluated to their results; no styling, formulas or other Spredin-specific metadata is included — it's the clean data, nothing else. It's the easiest format for another program or an AI agent to consume.

Export only. Spredin does not open arbitrary .json files (there's no single "right" way to lay an arbitrary JSON schema onto a grid). To bring JSON data in, convert it to .csv/.xlsx first, or parse it in the Python panel (import json) and sheet.write(...) the rows you want.

.csv / .tsv — one flat table of values#

The active sheet's computed values only. No formulas, no styling, no other sheets — just a grid of text. Best for piping raw data into another tool. .tsv uses tabs instead of commas.

Dates and times are written as they display (2024-01-15), so they read as dates in any tool and come back as dates when the file is opened here again. Other numbers are written plain — $1,200.50 becomes 1200.5 — which every tool reads as a number. Export → JSON follows the same rule.


Round-trip expectations (important)#


For agents#

Opening an untrusted spreadsheet is safe: Spredin never runs anything stored inside a file, and its readers are hardened against malformed input.

External-workbook references#

Excel writes a reference into another file as '[1]Sheet Name'!$A$1, where [1] indexes an external-link record. That file is not here, and Excel itself only holds a cached value for it until you refresh — so such a cell would import as #REF!.

Spredin redirects the reference to a local sheet of the same name when one exists, so the formula computes against the data in front of you. Measured on two real workbooks, every external reference named a sheet already present in the same file — the links pointed at an earlier copy of the same model. The count is reported in the status bar on open; it is never done silently.

A reference whose sheet is not present stays #REF!. Inventing a target would produce a confident wrong number, which is worse than an error.