Skip to content

Implement styles#538

Closed
ddimaria wants to merge 0 commit into
tafia:masterfrom
ddimaria:master
Closed

Implement styles#538
ddimaria wants to merge 0 commit into
tafia:masterfrom
ddimaria:master

Conversation

@ddimaria

@ddimaria ddimaria commented Jul 24, 2025

Copy link
Copy Markdown

Add style support for Calamine.

Basic api for getting styles

fn test_worksheet_style_iter() {
    let mut xlsx: Xlsx<_> = wb("styles.xlsx");
    let styles = xlsx.worksheet_style("Sheet 1").unwrap();

    for (row, styles) in styles.rows().enumerate() {
        for (col, style) in styles.iter().enumerate() {
            println!("row: {}, col: {}, style: {:?}", row, col, style);
        }
    }
}

Here is the relevant structs so far:

struct Style {
    font: Option<Font>,
    fill: Option<Fill>,
    borders: Option<Borders>,
    alignment: Option<Alignment>,
    number_format: Option<NumberFormat>,
    protection: Option<Protection>,
    style_id: Option<u32>,
}

struct Color {
    alpha: u8,
    red: u8,
    green: u8,
    blue: u8,
}

struct Borders {
    left: Border,
    right: Border,
    top: Border,
    bottom: Border,
    diagonal_down: Border,
    diagonal_up: Border,
}

struct Font {
    name: Option<String>,
    size: Option<f64>,
    weight: FontWeight,
    style: FontStyle,
    underline: UnderlineStyle,
    strikethrough: bool,
    color: Option<Color>,
    family: Option<String>,
}

struct Alignment {
    horizontal: HorizontalAlignment,
    vertical: VerticalAlignment,
    text_rotation: TextRotation,
    wrap_text: bool,
    indent: Option<u8>,
    shrink_to_fit: bool,
}

struct Fill {
    pattern: FillPattern,
    foreground_color: Option<Color>,
    background_color: Option<Color>,
}

struct NumberFormat {
    format_code: String,
    format_id: Option<u32>,
}

struct Protection {
    locked: bool,
    hidden: bool,
}

@jmcnamara

Copy link
Copy Markdown
Collaborator

Just a quick note to say that overall this looks good and I think it is going in the right direction.

I'll give more detailed feedback early next week.

For code comments you are already using correct sentence-case but could you also add a full stop/period at the end of each line. This makes it easier to change comments into documentation. It isn't a rule that has been adhered to in the existing code but I would like to improve that going forward.

@azarmadr

Copy link
Copy Markdown

Man, i wish i discovered this pull early, i wanted to know if a cell is stricken or not. Sat whole day and edited the code myself. Came till adding if stricken or not from the styles.

Learnt a lot from my experience though.
i'll test this pull against my code.

@azarmadr

Copy link
Copy Markdown

Is the data and styles coming together?
If not, then would we have to read the excel twice to get data, once and then style?

@ddimaria

Copy link
Copy Markdown
Author

Is the data and styles coming together? If not, then would we have to read the excel twice to get data, once and then style?

Data and Formats have always been processed separately (e.g. worksheet_range() and worksheet_formula()). This PR follows the format pattern (worksheet_style() for styles and worksheet_layout() for column widths and row heights).

When I finish the base work, I'm going to look into optimizations:

  1. More optimal storage (Vec vs Run-Length Encoding) for styles
  2. Looping (to your point, combining read operations to reduce memory/cpu usage)

@azarmadr

Copy link
Copy Markdown

i got it, even formulae were being used similarly. Was able to satisfy my requirement with worksheet_sytle over a range.

Another doubt, why every item in the style is an option?
All the fonts, borders, and others are available as ids in cellxfs?

hope i am not bothering you.😊

@ddimaria

Copy link
Copy Markdown
Author

Another doubt, why every item in the style is an option?

@azarmadr this is because the particular style may not exist for a given cell.

@ddimaria

ddimaria commented Aug 13, 2025

Copy link
Copy Markdown
Author

@jmcnamara We're currently using this fork in production and it's working well. What do I need to do to get this merged with Calamine? Some things to discuss:

  • Is the API agreeable?
  • What type of documentation is needed?
  • Add more testing?
  • Should this be a compiler feature flag?
  • This is only implemented for xlsx, is that OK?

@jmcnamara

Copy link
Copy Markdown
Collaborator

What do I need to do to get this merged with Calamine?

You should create a non-draft PR with all the WIP files and code removed. I will try to do a review on the current code.

Is the API agreeable?

Overall I think it is okay but I may have specific comments during review.

What type of documentation is needed?

Copy the style of the Xlsx documentation:

  • One line description.
  • One-two paragraph explanation.
  • Parameters section.
  • Errors section if the method Result<(), XlsxError>.
  • Examples section.

https://docs.rs/calamine/latest/calamine/struct.Xlsx.html#method.merged_regions_by_sheet

Add more testing?

That is always good. :-)

Should this be a compiler feature flag?

I think not.

This is only implemented for xlsx, is that OK?

Probably not but lets try get it right for Xlsx first.

Comment thread STYLE_FEATURE.md Outdated
@@ -0,0 +1,253 @@
# Style Support in Calamine

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Remove this files from the final PR or convert it to documentation.

Comment thread examples/style.rs Outdated
Comment thread src/datatype.rs Outdated
/// The cell value
pub value: Data,
/// The cell style
pub style: Option<Style>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Strictly speaking the style is never optional in the Excel file formats. There is always an implicit 0 style that is the default and is defined in the theme.xml file. Note, I'm not necessarily saying that it shouldn't be an Option<>.

Comment thread src/datatype.rs Outdated
self.style = None;
}

/// Check if the cell has style information

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Again, an Excel cell will always have style information even if it is implicit for size reasons.

Comment thread src/lib.rs Outdated
Comment thread src/lib.rs Outdated
Comment thread src/xlsx/cells_reader.rs Outdated
let mut style = None;

// Extract style ID if present
if let Ok(Some(style_id_str)) =

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Again, the style id is 0 and not None if not present.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is done, please review.

Comment thread src/xlsx/style_parser.rs Outdated
Comment thread src/xlsx/style_parser.rs Outdated
Comment thread src/style.rs Outdated
Comment thread src/style.rs Outdated
Comment thread src/xlsx/style_parser.rs Outdated
Comment thread src/style.rs Outdated
Comment thread src/style.rs Outdated
}

/// Get effective column width (custom or default)
pub fn get_effective_column_width(&self, column: u32) -> f64 {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This effective width is probably not the width that the user would expect and also may be dependent on the default font for the file.

See the following for the calculation in the other direction: https://github.com/jmcnamara/rust_xlsxwriter/blob/main/src/worksheet.rs#L19340-L19368

However, there may not be any simple way to return the correct value in all cases.

Comment thread src/style.rs Outdated
}

/// Get effective row height (custom or default)
pub fn get_effective_row_height(&self, row: u32) -> f64 {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Same comment for row height although the height calculation is simpler.

Comment thread src/xlsx/mod.rs Outdated
Comment thread test_indexed_colors.rs Outdated
Comment thread src/xlsx/style_parser.rs Outdated
use crate::style::*;
use crate::XlsxError;

/// Get default Excel 2007+ theme color scheme (Office theme)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Avoid using /// for non-public non-doc comments. Also this is an example of where proper sentence punctuation should be applied.

Comment thread tests/test.rs Outdated
@jmcnamara

jmcnamara commented Aug 14, 2025

Copy link
Copy Markdown
Collaborator

First review done. Some general comments:

  • Please squash and or rebase WIP code and reverts into a small number of logical commits or one commit.
  • Fix the clippy and or format warnings the are breaking the CI.
  • Add a full stop/period at the end of all comments.
  • Avoid /// comments for non-public non-doc comments. Use // instead.
  • Post an explanation here for the setting functions in CellData and Style.

@jmcnamara

Copy link
Copy Markdown
Collaborator

@ddimaria Any update on this?

@ddimaria

Copy link
Copy Markdown
Author

Finally back on this 😄

@ddimaria

Copy link
Copy Markdown
Author

I've merged main into this branch, resolving conflicts and fixing tests. I also have a 1M cell style file being used in new benchmark tests (used criterion). I'm moving on to flamegraph and will start improvements based on the results.

@ddimaria

Copy link
Copy Markdown
Author

I just implemented RLE and other optimizations. I'll close this PR and open a new one tomorrow, and will squash all of my commits. The perf is a lot better now, and there is less of a memory footprint.

@ddimaria

Copy link
Copy Markdown
Author

I added support for Rich Text, which handles various formats within a single cell. This is how excel stores it:

<si>
  <r>
    <rPr>
      <b/>  <!-- bold -->
      <sz val="11"/>
      <color rgb="FF0000FF"/>
      <rFont val="Calibri"/>
    </rPr>
    <t>Bold blue text</t>
  </r>
  <r>
    <rPr>
      <i/>  <!-- italic -->
      <sz val="11"/>
      <color rgb="FFFF0000"/>
    </rPr>
    <t> italic red text</t>
  </r>
</si>

wolfiesch added a commit to SynthGL/calamine that referenced this pull request Feb 14, 2026
Resolves merge conflicts in Cargo.toml (rstest version) and
tests/test.rs (pivot table tests vs style tests — keep both).

This merge adds:
- Style struct with Font/Fill/Borders/Alignment/NumberFormat/Protection
- styles.xml parser (src/xlsx/style_parser.rs)
- worksheet_style() API returning row×col style grid
- RLE-compressed StyleRange for memory efficiency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ddimaria

Copy link
Copy Markdown
Author

I noticed a bug in colors where tints weren't being handled. This is now implemented.

@ddimaria

ddimaria commented Mar 2, 2026

Copy link
Copy Markdown
Author

@jmcnamara I need to implement conditional formatting and validations, is it OK to add them to this PR?

@jmcnamara

Copy link
Copy Markdown
Collaborator

@ddimaria This PR is already very large. It would be best to get this stabilized first (is it suitable for review now?).

You can create other PRs that are dependent on this on. Conditional formatting and Data Validation should be separate PRs.

@ddimaria

ddimaria commented Mar 2, 2026

Copy link
Copy Markdown
Author

@ddimaria This PR is already very large. It would be best to get this stabilized first (is it suitable for review now?).

You can create other PRs that are dependent on this on. Conditional formatting and Data Validation should be separate PRs.

That works for me. I just rebased my fork's commits onto upstream/master and squashed into a single commit.
I'll do a final read through the code this morning and mark this ready for review.

@Erik1000

Copy link
Copy Markdown

@ddimaria This PR is already very large. It would be best to get this stabilized first (is it suitable for review now?).
You can create other PRs that are dependent on this on. Conditional formatting and Data Validation should be separate PRs.

That works for me. I just rebased my fork's commits onto upstream/master and squashed into a single commit. I'll do a final read through the code this morning and mark this ready for review.

is this PR now ready for review and can be merge? 👀

@ddimaria

Copy link
Copy Markdown
Author

@ddimaria This PR is already very large. It would be best to get this stabilized first (is it suitable for review now?).
You can create other PRs that are dependent on this on. Conditional formatting and Data Validation should be separate PRs.

That works for me. I just rebased my fork's commits onto upstream/master and squashed into a single commit. I'll do a final read through the code this morning and mark this ready for review.

is this PR now ready for review and can be merge? 👀

@Erik1000 I believe it is, though some conflicts just popped up. Let me review those and CI and it will be ready to review/merge.

@ddimaria ddimaria force-pushed the master branch 2 times, most recently from b01bbe7 to abca36f Compare March 17, 2026 14:52
@ddimaria ddimaria marked this pull request as ready for review March 17, 2026 14:53
@ddimaria

Copy link
Copy Markdown
Author

@jmcnamara this PR is ready for review. I'm staring conditional formatting in another branch.

@jmcnamara

Copy link
Copy Markdown
Collaborator

this PR is ready for review. I'm staring conditional formatting in another branch.

Ok. Fix the cargo fmt issues and if present, any clippy warnings. I think it would be best to resubmit this as a new PR. There are a lot of out of date comments on this PR.

@Erik1000

Erik1000 commented May 5, 2026

Copy link
Copy Markdown

@ddimaria will you submit a new PR or is there already an existing one?

@jmcnamara jmcnamara added the needs work for merge The PR needs some rework or clarification. No suitable for merge, yet. label May 10, 2026
@jmcnamara jmcnamara self-assigned this May 10, 2026
@jmcnamara jmcnamara added the awaiting user changes Awaiting changes to a PR to fix requested changes or CI issues. label May 10, 2026
@ddimaria

Copy link
Copy Markdown
Author

I'm back on this today. Will resolve conflicts and start a new PR

@ddimaria

Copy link
Copy Markdown
Author

PR moved to #653, it OK to close this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting user changes Awaiting changes to a PR to fix requested changes or CI issues. needs work for merge The PR needs some rework or clarification. No suitable for merge, yet.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants