xlsxturbo 0.9.0__tar.gz → 0.10.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,48 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.10.1] - 2026-01-16
9
+
10
+ ### Changed
11
+ - **Benchmark suite reorganization** - Moved benchmarks to `benchmarks/` directory
12
+ - New `benchmarks/benchmark.py` - comprehensive comparison vs polars, pandas+openpyxl, pandas+xlsxwriter
13
+ - Moved `benchmark_parallel.py` to `benchmarks/`
14
+ - Removed obsolete `benchmark.py` (referenced old Rust binary)
15
+ - **README Performance section** - Updated with reproducible benchmark methodology
16
+ - Changed performance claim from "~25x faster" to "~6x faster" (accurate for typical workloads)
17
+ - Added disclaimer that results vary by system
18
+ - Linked to Benchmarking section for running your own tests
19
+
20
+ ## [0.10.0] - 2026-01-16
21
+
22
+ ### Added
23
+ - **Comments/Notes** - Add cell annotations with optional author
24
+ - Simple text: `comments={'A1': 'Note text'}`
25
+ - With author: `comments={'A1': {'text': 'Note', 'author': 'John'}}`
26
+ - Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
27
+ - **Data Validation** - Add dropdowns and constraints to columns
28
+ - List (dropdown): `validations={'Status': {'type': 'list', 'values': ['Open', 'Closed']}}`
29
+ - Whole number: `validations={'Score': {'type': 'whole_number', 'min': 0, 'max': 100}}`
30
+ - Decimal: `validations={'Price': {'type': 'decimal', 'min': 0.0, 'max': 999.99}}`
31
+ - Text length: `validations={'Code': {'type': 'text_length', 'min': 3, 'max': 10}}`
32
+ - Supports input/error messages: `input_title`, `input_message`, `error_title`, `error_message`
33
+ - Supports column patterns (like `column_formats`)
34
+ - Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
35
+ - **Rich Text** - Multiple formats within a single cell
36
+ - Format segments: `rich_text={'A1': [('Bold', {'bold': True}), ' normal text']}`
37
+ - Supports: `bold`, `italic`, `font_color`, `bg_color`, `font_size`, `underline`
38
+ - Mix formatted and plain text segments
39
+ - Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
40
+ - **Images** - Embed PNG, JPEG, GIF, BMP images in cells
41
+ - Simple path: `images={'B5': 'logo.png'}`
42
+ - With options: `images={'B5': {'path': 'logo.png', 'scale_width': 0.5, 'scale_height': 0.5}}`
43
+ - Options: `path`, `scale_width`, `scale_height`, `alt_text`
44
+ - Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
45
+
46
+ ### Notes
47
+ - All new features are disabled in `constant_memory` mode (they require random access)
48
+ - Data validation list values are limited to 255 total characters (Excel limitation)
49
+
8
50
  ## [0.9.0] - 2026-01-15
9
51
 
10
52
  ### Added
@@ -171,6 +213,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
171
213
  - Support for custom sheet names
172
214
  - Verbose mode for progress reporting
173
215
 
216
+ [0.10.1]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.10.1
217
+ [0.10.0]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.10.0
174
218
  [0.9.0]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.9.0
175
219
  [0.8.0]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.8.0
176
220
  [0.7.0]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.7.0
@@ -673,9 +673,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
673
673
 
674
674
  [[package]]
675
675
  name = "wasip2"
676
- version = "1.0.1+wasi-0.2.4"
676
+ version = "1.0.2+wasi-0.2.9"
677
677
  source = "registry+https://github.com/rust-lang/crates.io-index"
678
- checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
678
+ checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
679
679
  dependencies = [
680
680
  "wit-bindgen",
681
681
  ]
@@ -795,13 +795,13 @@ dependencies = [
795
795
 
796
796
  [[package]]
797
797
  name = "wit-bindgen"
798
- version = "0.46.0"
798
+ version = "0.51.0"
799
799
  source = "registry+https://github.com/rust-lang/crates.io-index"
800
- checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
800
+ checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
801
801
 
802
802
  [[package]]
803
803
  name = "xlsxturbo"
804
- version = "0.9.0"
804
+ version = "0.10.1"
805
805
  dependencies = [
806
806
  "chrono",
807
807
  "clap",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "xlsxturbo"
3
- version = "0.9.0"
3
+ version = "0.10.1"
4
4
  edition = "2021"
5
5
  description = "High-performance Excel writer with automatic type detection (pandas, polars, CSV)"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xlsxturbo
3
- Version: 0.9.0
3
+ Version: 0.10.1
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: Intended Audience :: Science/Research
@@ -43,6 +43,10 @@ High-performance Excel writer with automatic type detection. Written in Rust, us
43
43
  - **Formula columns** - add calculated columns with Excel formulas
44
44
  - **Merged cells** - merge cell ranges for headers and titles
45
45
  - **Hyperlinks** - add clickable links to cells
46
+ - **Comments/Notes** - add cell annotations with optional author
47
+ - **Data validation** - dropdowns, number ranges, text length constraints
48
+ - **Rich text** - multiple formats within a single cell
49
+ - **Images** - embed PNG, JPEG, GIF, BMP in cells
46
50
  - **Auto-fit columns** - automatically adjust column widths to fit content
47
51
  - **Custom column widths** - set specific widths per column or cap all with _all
48
52
  - **Header styling** - bold, colors, font size for header row
@@ -60,7 +64,7 @@ High-performance Excel writer with automatic type detection. Written in Rust, us
60
64
  - Datetimes (ISO 8601) → Excel datetimes
61
65
  - `NaN`/`Inf` → Empty cells (graceful handling)
62
66
  - Everything else → Text
63
- - **~25x faster** than pandas + openpyxl
67
+ - **~6x faster** than pandas + openpyxl (see [benchmarks](#performance))
64
68
  - **Memory efficient** - streams data with 1MB buffer
65
69
  - Available as both **Python library** and **CLI tool**
66
70
 
@@ -345,6 +349,10 @@ Available per-sheet options:
345
349
  - `formula_columns` (dict): Calculated columns with Excel formulas (column name -> formula template)
346
350
  - `merged_ranges` (list): List of (range, text) or (range, text, format) tuples to merge cells
347
351
  - `hyperlinks` (list): List of (cell, url) or (cell, url, display_text) tuples to add clickable links
352
+ - `comments` (dict): Cell comments/notes (cell_ref -> text or {text, author})
353
+ - `validations` (dict): Data validation rules (column name/pattern -> validation config)
354
+ - `rich_text` (dict): Rich text with multiple formats (cell_ref -> list of segments)
355
+ - `images` (dict): Embedded images (cell_ref -> path or {path, scale_width, scale_height, alt_text})
348
356
 
349
357
  ### Conditional Formatting
350
358
 
@@ -515,6 +523,193 @@ xlsxturbo.df_to_xlsx(df, "companies.xlsx",
515
523
  - Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
516
524
  - Not available in constant memory mode
517
525
 
526
+ ### Comments/Notes
527
+
528
+ Add cell annotations (hover to view):
529
+
530
+ ```python
531
+ import xlsxturbo
532
+ import pandas as pd
533
+
534
+ df = pd.DataFrame({
535
+ 'product': ['Widget A', 'Widget B'],
536
+ 'price': [19.99, 29.99]
537
+ })
538
+
539
+ xlsxturbo.df_to_xlsx(df, "report.xlsx",
540
+ comments={
541
+ # Simple text comment
542
+ 'A1': 'This column contains product names',
543
+ # Comment with author
544
+ 'B1': {'text': 'Prices in USD', 'author': 'Finance Team'}
545
+ }
546
+ )
547
+ ```
548
+
549
+ **Comment format:**
550
+ - Simple: `{'A1': 'Note text'}`
551
+ - With author: `{'A1': {'text': 'Note text', 'author': 'Name'}}`
552
+
553
+ **Notes:**
554
+ - Comments appear as small red triangles in the cell corner
555
+ - Hover over the cell to see the comment
556
+ - Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
557
+ - Not available in constant memory mode
558
+
559
+ ### Data Validation
560
+
561
+ Add dropdowns and input constraints:
562
+
563
+ ```python
564
+ import xlsxturbo
565
+ import pandas as pd
566
+
567
+ df = pd.DataFrame({
568
+ 'status': ['Open', 'Closed'],
569
+ 'score': [85, 92],
570
+ 'price': [19.99, 29.99],
571
+ 'code': ['ABC', 'XYZ']
572
+ })
573
+
574
+ xlsxturbo.df_to_xlsx(df, "validated.xlsx",
575
+ validations={
576
+ # Dropdown list
577
+ 'status': {
578
+ 'type': 'list',
579
+ 'values': ['Open', 'Closed', 'Pending', 'Review']
580
+ },
581
+ # Whole number range (0-100)
582
+ 'score': {
583
+ 'type': 'whole_number',
584
+ 'min': 0,
585
+ 'max': 100,
586
+ 'error_title': 'Invalid Score',
587
+ 'error_message': 'Score must be between 0 and 100'
588
+ },
589
+ # Decimal range
590
+ 'price': {
591
+ 'type': 'decimal',
592
+ 'min': 0.0,
593
+ 'max': 999.99
594
+ },
595
+ # Text length constraint
596
+ 'code': {
597
+ 'type': 'text_length',
598
+ 'min': 3,
599
+ 'max': 10
600
+ }
601
+ }
602
+ )
603
+ ```
604
+
605
+ **Validation types:**
606
+
607
+ | Type | Aliases | Description | Options |
608
+ |------|---------|-------------|---------|
609
+ | `list` | - | Dropdown menu | `values` (list of strings, max 255 chars total) |
610
+ | `whole_number` | `whole`, `integer` | Integer range | `min`, `max` |
611
+ | `decimal` | `number` | Decimal range | `min`, `max` |
612
+ | `text_length` | `textlength`, `length` | Character count | `min`, `max` |
613
+
614
+ **Optional message options:**
615
+ - `input_title`, `input_message`: Prompt shown when cell is selected
616
+ - `error_title`, `error_message`: Message shown when invalid data is entered
617
+
618
+ **Notes:**
619
+ - Validations apply to the data rows of the specified column
620
+ - Column patterns work: `'score_*': {...}` matches all columns starting with `score_`
621
+ - If only `min` or only `max` is specified, the other defaults to the type's extreme value
622
+ - List validation values are limited to 255 total characters (Excel limitation)
623
+ - Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
624
+ - Not available in constant memory mode
625
+
626
+ ### Rich Text
627
+
628
+ Multiple formats within a single cell:
629
+
630
+ ```python
631
+ import xlsxturbo
632
+ import pandas as pd
633
+
634
+ df = pd.DataFrame({'A': [1, 2, 3]})
635
+
636
+ xlsxturbo.df_to_xlsx(df, "rich.xlsx",
637
+ rich_text={
638
+ 'D1': [
639
+ ('Important: ', {'bold': True, 'font_color': 'red'}),
640
+ 'Please review ',
641
+ ('all', {'italic': True}),
642
+ ' values'
643
+ ],
644
+ 'D2': [
645
+ ('Status: ', {'bold': True}),
646
+ ('OK', {'font_color': 'green', 'bold': True})
647
+ ]
648
+ }
649
+ )
650
+ ```
651
+
652
+ **Segment format:**
653
+ - Formatted: `('text', {'bold': True, 'font_color': 'blue'})`
654
+ - Plain: `'plain text'` (no formatting)
655
+
656
+ **Available format options:**
657
+ - `bold` (bool)
658
+ - `italic` (bool)
659
+ - `font_color` (str): '#RRGGBB' or named color
660
+ - `bg_color` (str): Background color
661
+ - `font_size` (float)
662
+ - `underline` (bool)
663
+
664
+ **Notes:**
665
+ - Rich text writes to the specified cell position (overwrites existing content)
666
+ - Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
667
+ - Not available in constant memory mode
668
+
669
+ ### Images
670
+
671
+ Embed images in cells:
672
+
673
+ ```python
674
+ import xlsxturbo
675
+ import pandas as pd
676
+
677
+ df = pd.DataFrame({'Product': ['Widget A', 'Widget B'], 'Price': [19.99, 29.99]})
678
+
679
+ xlsxturbo.df_to_xlsx(df, "catalog.xlsx",
680
+ autofit=True,
681
+ images={
682
+ # Simple path
683
+ 'C2': 'images/widget_a.png',
684
+ # With options
685
+ 'C3': {
686
+ 'path': 'images/widget_b.png',
687
+ 'scale_width': 0.5,
688
+ 'scale_height': 0.5,
689
+ 'alt_text': 'Widget B photo'
690
+ }
691
+ }
692
+ )
693
+ ```
694
+
695
+ **Image format:**
696
+ - Simple: `{'C2': 'path/to/image.png'}`
697
+ - With options: `{'C2': {'path': '...', 'scale_width': 0.5, ...}}`
698
+
699
+ **Available options:**
700
+ - `path` (str, required): Path to image file
701
+ - `scale_width` (float): Width scale factor (1.0 = original)
702
+ - `scale_height` (float): Height scale factor (1.0 = original)
703
+ - `alt_text` (str): Alternative text for accessibility
704
+
705
+ **Supported formats:** PNG, JPEG, GIF, BMP
706
+
707
+ **Notes:**
708
+ - Images are positioned at the specified cell (overlays any existing content)
709
+ - Image file must exist; non-existent files will raise an error
710
+ - Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
711
+ - Not available in constant memory mode
712
+
518
713
  ### Constant Memory Mode (Large Files)
519
714
 
520
715
  For very large files (millions of rows), use `constant_memory=True` to minimize RAM usage:
@@ -542,6 +737,13 @@ xlsxturbo.dfs_to_xlsx([
542
737
  - `table_style` (Excel tables)
543
738
  - `freeze_panes`
544
739
  - `row_heights`
740
+ - `conditional_formats`
741
+ - `merged_ranges`
742
+ - `hyperlinks`
743
+ - `comments`
744
+ - `validations`
745
+ - `rich_text`
746
+ - `images`
545
747
  - `autofit`
546
748
  - `conditional_formats`
547
749
  - `formula_columns`
@@ -605,15 +807,16 @@ xlsxturbo sales.csv report.xlsx -d eu -v --sheet-name "Q4 Sales"
605
807
 
606
808
  ## Performance
607
809
 
608
- Benchmarked on 525,684 rows x 98 columns:
810
+ *Reference benchmark on 100,000 rows x 50 columns with mixed data types. Your results will vary by system - run the benchmark yourself (see [Benchmarking](#benchmarking)).*
811
+
812
+ | Library | Time (s) | Rows/sec | vs xlsxturbo |
813
+ |---------|----------|----------|--------------|
814
+ | **xlsxturbo** | **6.65** | **15,033** | **1.0x** |
815
+ | polars | 25.07 | 3,988 | 3.8x |
816
+ | pandas + xlsxwriter | 35.60 | 2,809 | 5.4x |
817
+ | pandas + openpyxl | 38.85 | 2,574 | 5.8x |
609
818
 
610
- | Method | Time | Speedup |
611
- |--------|------|---------|
612
- | **xlsxturbo** | 28.5s | **26.7x** |
613
- | PyExcelerate | 107s | 7.1x |
614
- | pandas + xlsxwriter | 374s | 2.0x |
615
- | pandas + openpyxl | 762s | 1.0x |
616
- | polars.write_excel | 1039s | 0.7x |
819
+ *Test system: Windows 11, Python 3.14, AMD Ryzen 9 (32 threads)*
617
820
 
618
821
  ## Type Detection Examples
619
822
 
@@ -649,14 +852,24 @@ maturin build --release
649
852
 
650
853
  ## Benchmarking
651
854
 
652
- Run the included benchmark script:
855
+ Run the included benchmark scripts:
653
856
 
654
857
  ```bash
655
- # Default: 100K rows x 50 columns
656
- python benchmark.py
858
+ # Compare xlsxturbo vs other libraries (100K rows default)
859
+ python benchmarks/benchmark.py
860
+
861
+ # Full benchmark: small, medium, large datasets
862
+ python benchmarks/benchmark.py --full
657
863
 
658
864
  # Custom size
659
- python benchmark.py --rows 500000 --cols 100
865
+ python benchmarks/benchmark.py --rows 500000 --cols 100
866
+
867
+ # Output formats for CI/documentation
868
+ python benchmarks/benchmark.py --markdown
869
+ python benchmarks/benchmark.py --json
870
+
871
+ # Test parallel vs single-threaded CSV conversion
872
+ python benchmarks/benchmark_parallel.py
660
873
  ```
661
874
 
662
875
  ## License
@@ -10,6 +10,10 @@ High-performance Excel writer with automatic type detection. Written in Rust, us
10
10
  - **Formula columns** - add calculated columns with Excel formulas
11
11
  - **Merged cells** - merge cell ranges for headers and titles
12
12
  - **Hyperlinks** - add clickable links to cells
13
+ - **Comments/Notes** - add cell annotations with optional author
14
+ - **Data validation** - dropdowns, number ranges, text length constraints
15
+ - **Rich text** - multiple formats within a single cell
16
+ - **Images** - embed PNG, JPEG, GIF, BMP in cells
13
17
  - **Auto-fit columns** - automatically adjust column widths to fit content
14
18
  - **Custom column widths** - set specific widths per column or cap all with _all
15
19
  - **Header styling** - bold, colors, font size for header row
@@ -27,7 +31,7 @@ High-performance Excel writer with automatic type detection. Written in Rust, us
27
31
  - Datetimes (ISO 8601) → Excel datetimes
28
32
  - `NaN`/`Inf` → Empty cells (graceful handling)
29
33
  - Everything else → Text
30
- - **~25x faster** than pandas + openpyxl
34
+ - **~6x faster** than pandas + openpyxl (see [benchmarks](#performance))
31
35
  - **Memory efficient** - streams data with 1MB buffer
32
36
  - Available as both **Python library** and **CLI tool**
33
37
 
@@ -312,6 +316,10 @@ Available per-sheet options:
312
316
  - `formula_columns` (dict): Calculated columns with Excel formulas (column name -> formula template)
313
317
  - `merged_ranges` (list): List of (range, text) or (range, text, format) tuples to merge cells
314
318
  - `hyperlinks` (list): List of (cell, url) or (cell, url, display_text) tuples to add clickable links
319
+ - `comments` (dict): Cell comments/notes (cell_ref -> text or {text, author})
320
+ - `validations` (dict): Data validation rules (column name/pattern -> validation config)
321
+ - `rich_text` (dict): Rich text with multiple formats (cell_ref -> list of segments)
322
+ - `images` (dict): Embedded images (cell_ref -> path or {path, scale_width, scale_height, alt_text})
315
323
 
316
324
  ### Conditional Formatting
317
325
 
@@ -482,6 +490,193 @@ xlsxturbo.df_to_xlsx(df, "companies.xlsx",
482
490
  - Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
483
491
  - Not available in constant memory mode
484
492
 
493
+ ### Comments/Notes
494
+
495
+ Add cell annotations (hover to view):
496
+
497
+ ```python
498
+ import xlsxturbo
499
+ import pandas as pd
500
+
501
+ df = pd.DataFrame({
502
+ 'product': ['Widget A', 'Widget B'],
503
+ 'price': [19.99, 29.99]
504
+ })
505
+
506
+ xlsxturbo.df_to_xlsx(df, "report.xlsx",
507
+ comments={
508
+ # Simple text comment
509
+ 'A1': 'This column contains product names',
510
+ # Comment with author
511
+ 'B1': {'text': 'Prices in USD', 'author': 'Finance Team'}
512
+ }
513
+ )
514
+ ```
515
+
516
+ **Comment format:**
517
+ - Simple: `{'A1': 'Note text'}`
518
+ - With author: `{'A1': {'text': 'Note text', 'author': 'Name'}}`
519
+
520
+ **Notes:**
521
+ - Comments appear as small red triangles in the cell corner
522
+ - Hover over the cell to see the comment
523
+ - Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
524
+ - Not available in constant memory mode
525
+
526
+ ### Data Validation
527
+
528
+ Add dropdowns and input constraints:
529
+
530
+ ```python
531
+ import xlsxturbo
532
+ import pandas as pd
533
+
534
+ df = pd.DataFrame({
535
+ 'status': ['Open', 'Closed'],
536
+ 'score': [85, 92],
537
+ 'price': [19.99, 29.99],
538
+ 'code': ['ABC', 'XYZ']
539
+ })
540
+
541
+ xlsxturbo.df_to_xlsx(df, "validated.xlsx",
542
+ validations={
543
+ # Dropdown list
544
+ 'status': {
545
+ 'type': 'list',
546
+ 'values': ['Open', 'Closed', 'Pending', 'Review']
547
+ },
548
+ # Whole number range (0-100)
549
+ 'score': {
550
+ 'type': 'whole_number',
551
+ 'min': 0,
552
+ 'max': 100,
553
+ 'error_title': 'Invalid Score',
554
+ 'error_message': 'Score must be between 0 and 100'
555
+ },
556
+ # Decimal range
557
+ 'price': {
558
+ 'type': 'decimal',
559
+ 'min': 0.0,
560
+ 'max': 999.99
561
+ },
562
+ # Text length constraint
563
+ 'code': {
564
+ 'type': 'text_length',
565
+ 'min': 3,
566
+ 'max': 10
567
+ }
568
+ }
569
+ )
570
+ ```
571
+
572
+ **Validation types:**
573
+
574
+ | Type | Aliases | Description | Options |
575
+ |------|---------|-------------|---------|
576
+ | `list` | - | Dropdown menu | `values` (list of strings, max 255 chars total) |
577
+ | `whole_number` | `whole`, `integer` | Integer range | `min`, `max` |
578
+ | `decimal` | `number` | Decimal range | `min`, `max` |
579
+ | `text_length` | `textlength`, `length` | Character count | `min`, `max` |
580
+
581
+ **Optional message options:**
582
+ - `input_title`, `input_message`: Prompt shown when cell is selected
583
+ - `error_title`, `error_message`: Message shown when invalid data is entered
584
+
585
+ **Notes:**
586
+ - Validations apply to the data rows of the specified column
587
+ - Column patterns work: `'score_*': {...}` matches all columns starting with `score_`
588
+ - If only `min` or only `max` is specified, the other defaults to the type's extreme value
589
+ - List validation values are limited to 255 total characters (Excel limitation)
590
+ - Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
591
+ - Not available in constant memory mode
592
+
593
+ ### Rich Text
594
+
595
+ Multiple formats within a single cell:
596
+
597
+ ```python
598
+ import xlsxturbo
599
+ import pandas as pd
600
+
601
+ df = pd.DataFrame({'A': [1, 2, 3]})
602
+
603
+ xlsxturbo.df_to_xlsx(df, "rich.xlsx",
604
+ rich_text={
605
+ 'D1': [
606
+ ('Important: ', {'bold': True, 'font_color': 'red'}),
607
+ 'Please review ',
608
+ ('all', {'italic': True}),
609
+ ' values'
610
+ ],
611
+ 'D2': [
612
+ ('Status: ', {'bold': True}),
613
+ ('OK', {'font_color': 'green', 'bold': True})
614
+ ]
615
+ }
616
+ )
617
+ ```
618
+
619
+ **Segment format:**
620
+ - Formatted: `('text', {'bold': True, 'font_color': 'blue'})`
621
+ - Plain: `'plain text'` (no formatting)
622
+
623
+ **Available format options:**
624
+ - `bold` (bool)
625
+ - `italic` (bool)
626
+ - `font_color` (str): '#RRGGBB' or named color
627
+ - `bg_color` (str): Background color
628
+ - `font_size` (float)
629
+ - `underline` (bool)
630
+
631
+ **Notes:**
632
+ - Rich text writes to the specified cell position (overwrites existing content)
633
+ - Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
634
+ - Not available in constant memory mode
635
+
636
+ ### Images
637
+
638
+ Embed images in cells:
639
+
640
+ ```python
641
+ import xlsxturbo
642
+ import pandas as pd
643
+
644
+ df = pd.DataFrame({'Product': ['Widget A', 'Widget B'], 'Price': [19.99, 29.99]})
645
+
646
+ xlsxturbo.df_to_xlsx(df, "catalog.xlsx",
647
+ autofit=True,
648
+ images={
649
+ # Simple path
650
+ 'C2': 'images/widget_a.png',
651
+ # With options
652
+ 'C3': {
653
+ 'path': 'images/widget_b.png',
654
+ 'scale_width': 0.5,
655
+ 'scale_height': 0.5,
656
+ 'alt_text': 'Widget B photo'
657
+ }
658
+ }
659
+ )
660
+ ```
661
+
662
+ **Image format:**
663
+ - Simple: `{'C2': 'path/to/image.png'}`
664
+ - With options: `{'C2': {'path': '...', 'scale_width': 0.5, ...}}`
665
+
666
+ **Available options:**
667
+ - `path` (str, required): Path to image file
668
+ - `scale_width` (float): Width scale factor (1.0 = original)
669
+ - `scale_height` (float): Height scale factor (1.0 = original)
670
+ - `alt_text` (str): Alternative text for accessibility
671
+
672
+ **Supported formats:** PNG, JPEG, GIF, BMP
673
+
674
+ **Notes:**
675
+ - Images are positioned at the specified cell (overlays any existing content)
676
+ - Image file must exist; non-existent files will raise an error
677
+ - Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
678
+ - Not available in constant memory mode
679
+
485
680
  ### Constant Memory Mode (Large Files)
486
681
 
487
682
  For very large files (millions of rows), use `constant_memory=True` to minimize RAM usage:
@@ -509,6 +704,13 @@ xlsxturbo.dfs_to_xlsx([
509
704
  - `table_style` (Excel tables)
510
705
  - `freeze_panes`
511
706
  - `row_heights`
707
+ - `conditional_formats`
708
+ - `merged_ranges`
709
+ - `hyperlinks`
710
+ - `comments`
711
+ - `validations`
712
+ - `rich_text`
713
+ - `images`
512
714
  - `autofit`
513
715
  - `conditional_formats`
514
716
  - `formula_columns`
@@ -572,15 +774,16 @@ xlsxturbo sales.csv report.xlsx -d eu -v --sheet-name "Q4 Sales"
572
774
 
573
775
  ## Performance
574
776
 
575
- Benchmarked on 525,684 rows x 98 columns:
777
+ *Reference benchmark on 100,000 rows x 50 columns with mixed data types. Your results will vary by system - run the benchmark yourself (see [Benchmarking](#benchmarking)).*
778
+
779
+ | Library | Time (s) | Rows/sec | vs xlsxturbo |
780
+ |---------|----------|----------|--------------|
781
+ | **xlsxturbo** | **6.65** | **15,033** | **1.0x** |
782
+ | polars | 25.07 | 3,988 | 3.8x |
783
+ | pandas + xlsxwriter | 35.60 | 2,809 | 5.4x |
784
+ | pandas + openpyxl | 38.85 | 2,574 | 5.8x |
576
785
 
577
- | Method | Time | Speedup |
578
- |--------|------|---------|
579
- | **xlsxturbo** | 28.5s | **26.7x** |
580
- | PyExcelerate | 107s | 7.1x |
581
- | pandas + xlsxwriter | 374s | 2.0x |
582
- | pandas + openpyxl | 762s | 1.0x |
583
- | polars.write_excel | 1039s | 0.7x |
786
+ *Test system: Windows 11, Python 3.14, AMD Ryzen 9 (32 threads)*
584
787
 
585
788
  ## Type Detection Examples
586
789
 
@@ -616,14 +819,24 @@ maturin build --release
616
819
 
617
820
  ## Benchmarking
618
821
 
619
- Run the included benchmark script:
822
+ Run the included benchmark scripts:
620
823
 
621
824
  ```bash
622
- # Default: 100K rows x 50 columns
623
- python benchmark.py
825
+ # Compare xlsxturbo vs other libraries (100K rows default)
826
+ python benchmarks/benchmark.py
827
+
828
+ # Full benchmark: small, medium, large datasets
829
+ python benchmarks/benchmark.py --full
624
830
 
625
831
  # Custom size
626
- python benchmark.py --rows 500000 --cols 100
832
+ python benchmarks/benchmark.py --rows 500000 --cols 100
833
+
834
+ # Output formats for CI/documentation
835
+ python benchmarks/benchmark.py --markdown
836
+ python benchmarks/benchmark.py --json
837
+
838
+ # Test parallel vs single-threaded CSV conversion
839
+ python benchmarks/benchmark_parallel.py
627
840
  ```
628
841
 
629
842
  ## License