Skip to content

Support named styles#65

Merged
borgar merged 6 commits intoborgar:masterfrom
atlipall:named-styles
Apr 13, 2026
Merged

Support named styles#65
borgar merged 6 commits intoborgar:masterfrom
atlipall:named-styles

Conversation

@atlipall
Copy link
Copy Markdown
Contributor

@atlipall atlipall commented Apr 8, 2026

Summary

  • Parse OOXML <cellStyles> elements and emit a flat namedStyles array on the workbook
  • Styles that inherit from a non-default named style get a parentStyle property referencing the style by name
  • All style properties are flattened directly onto the named style object (no nested style wrapper)
  • Backward compatible: namedStyles is only emitted when there are custom named styles beyond the default "Normal"

Example JSF output

{
  "namedStyles": [
    { "builtinId": 0, "name": "Normal", "fontScheme": "minor", "fontSize": 12 },
    { "builtinId": 5, "name": "Percent", "fontScheme": "minor", "fontSize": 12, "numberFormat": "0%" }
  ],
  "styles": [
    { "fontScheme": "minor", "fontSize": 12 },
    { "fontScheme": "minor", "fontSize": 12, "numberFormat": "0.00E+00" },
    { "parentStyle": "Percent", "fontScheme": "minor", "fontSize": 12, "numberFormat": "0.0%" }
  ]
}
  • namedStyles contains the named style definitions (flat: style properties are at the top level alongside name and builtinId)
  • parentStyle on a style references a named style by name (omitted when inheriting from "Normal", the default)
  • Workbooks with only a "Normal" named style do not emit namedStyles (no fixture changes for existing tests)

Parse OOXML <cellStyles> elements and emit a flat namedStyles array on the
workbook. Styles that inherit from a non-default named style get a
parentStyle property with the style name (e.g. "Percent", "Heading 1").
@atlipall atlipall changed the title feat: support named styles and parentStyle references Support named styles Apr 8, 2026
atlipall added 2 commits April 8, 2026 14:09
… NamedStyle

Only emit namedStyles when there are custom named styles beyond the
default Normal — this keeps output backward compatible for workbooks
with no custom named styles. Also rename CellStyle type to NamedStyle
to match the updated @jsfkit/types.
Copy link
Copy Markdown
Owner

@borgar borgar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since a renderer may be looking up styles per-cell, per-render, performance can matter here. I think it would be better to use a record for the named styles:

st1 = { parentStyle: 'Normal', fontSize: 16 };
st2 = wb.namedStyles[st1.parentStyle];

This is a lot faster and easier than having to do:

st1 = { parentStyle: 'Normal', fontSize: 16 };
st2 = wb.namedStyles.find(item => item.name === st1.parentStyle);

A renderer could obviously maintain its own internal map/record but if this is a record already, the lookups can happen instantly and there is less implementation demand placed on the client.

Alternatively, we could use numerical indexes into the list:

st1 = { parentStyle: 0, fontSize: 16 };
st2 = wb.namedStyles[st1.parentStyle];

This is the same as how cells index into the style list so it is a familiar pattern, but its less nice for humans, and we do have the names here. The names map also has the advantage of ensuring that names are unique.

So, the changes I suggest:

  1. Rename the parentStyle property to extends.
  2. Use an object for namedStyles, types as Record<string, NamedStyle>.

What that would look like:

namedStyles: {
  Normal: { name: 'Normal', presetId: 0, fontScheme: 'minor', fontSize: 12 },
  Percent: { name: 'Percent', builtinId: 5, fontScheme: 'minor', fontSize: 12, numberFormat: '0%' },
},
styles: [
  { fontScheme: 'minor', fontSize: 12 },
  { fontScheme: 'minor', fontSize: 12, numberFormat: '0.00E+00' },
  { extends: 'Percent', fontScheme: 'minor', fontSize: 12, numberFormat: '0.0%' },
],

I realize we're then triplicating the name (in Style, NamedStyle, and in the map, but it's important to include it because otherwise the two style types are hard to tell apart.

atlipall added 2 commits April 9, 2026 14:21
…e to extendsStyle

- namedStyles is now Record<string, NamedStyle> instead of NamedStyle[]
- Rename parentStyle to extendsStyle to avoid reserved keyword conflict
- Rename CellStyleEntry to NamedStyleEntry for consistency
@atlipall atlipall marked this pull request as ready for review April 10, 2026 15:35
@borgar borgar merged commit 0b3f05c into borgar:master Apr 13, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants