xlsxturbo 0.8.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,76 @@ 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
+
50
+ ## [0.9.0] - 2026-01-15
51
+
52
+ ### Added
53
+ - **Conditional formatting** - Visual formatting based on cell values
54
+ - `2_color_scale`: Gradient from min_color to max_color
55
+ - `3_color_scale`: Three-color gradient with min/mid/max colors
56
+ - `data_bar`: In-cell bar chart with customizable color, direction, solid fill
57
+ - `icon_set`: Traffic lights, arrows, flags (3/4/5 icons), with reverse and icons_only options
58
+ - Supports column name patterns: `'price_*': {'type': 'data_bar', ...}`
59
+ - Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
60
+ - Example: `conditional_formats={'score': {'type': '2_color_scale', 'min_color': '#FF0000', 'max_color': '#00FF00'}}`
61
+ - **Formula columns** - Add calculated columns with Excel formulas
62
+ - Use `{row}` placeholder for row numbers (1-based)
63
+ - Columns appear after data columns
64
+ - Order preserved (first formula = first new column)
65
+ - Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
66
+ - Example: `formula_columns={'Total': '=A{row}+B{row}', 'Percentage': '=C{row}/D{row}*100'}`
67
+ - **Merged cells** - Merge cell ranges for headers, titles, and grouped labels
68
+ - Uses Excel notation for ranges (e.g., 'A1:D1')
69
+ - Optional formatting with HeaderFormat options (bold, colors, etc.)
70
+ - Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
71
+ - Example: `merged_ranges=[('A1:C1', 'Title'), ('A2:C2', 'Subtitle', {'bold': True})]`
72
+ - **Hyperlinks** - Add clickable links to cells
73
+ - Uses Excel notation for cell reference (e.g., 'A1', 'B5')
74
+ - Optional display text (defaults to URL if not provided)
75
+ - Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
76
+ - Example: `hyperlinks=[('A2', 'https://example.com'), ('B2', 'https://google.com', 'Google')]`
77
+
8
78
  ## [0.8.0] - 2026-01-15
9
79
 
10
80
  ### Added
@@ -143,6 +213,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
143
213
  - Support for custom sheet names
144
214
  - Verbose mode for progress reporting
145
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
218
+ [0.9.0]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.9.0
146
219
  [0.8.0]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.8.0
147
220
  [0.7.0]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.7.0
148
221
  [0.6.0]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.6.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.8.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.8.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.8.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
@@ -39,6 +39,14 @@ High-performance Excel writer with automatic type detection. Written in Rust, us
39
39
 
40
40
  - **Direct DataFrame support** for pandas and polars
41
41
  - **Excel tables** - filterable tables with 61 built-in styles (banded rows, autofilter)
42
+ - **Conditional formatting** - color scales, data bars, icon sets for visual data analysis
43
+ - **Formula columns** - add calculated columns with Excel formulas
44
+ - **Merged cells** - merge cell ranges for headers and titles
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
42
50
  - **Auto-fit columns** - automatically adjust column widths to fit content
43
51
  - **Custom column widths** - set specific widths per column or cap all with _all
44
52
  - **Header styling** - bold, colors, font size for header row
@@ -56,7 +64,7 @@ High-performance Excel writer with automatic type detection. Written in Rust, us
56
64
  - Datetimes (ISO 8601) → Excel datetimes
57
65
  - `NaN`/`Inf` → Empty cells (graceful handling)
58
66
  - Everything else → Text
59
- - **~25x faster** than pandas + openpyxl
67
+ - **~6x faster** than pandas + openpyxl (see [benchmarks](#performance))
60
68
  - **Memory efficient** - streams data with 1MB buffer
61
69
  - Available as both **Python library** and **CLI tool**
62
70
 
@@ -337,6 +345,370 @@ Available per-sheet options:
337
345
  - `table_name` (str): Custom Excel table name
338
346
  - `header_format` (dict): Header cell styling
339
347
  - `column_formats` (dict): Column formatting with pattern matching
348
+ - `conditional_formats` (dict): Conditional formatting (color scales, data bars, icons)
349
+ - `formula_columns` (dict): Calculated columns with Excel formulas (column name -> formula template)
350
+ - `merged_ranges` (list): List of (range, text) or (range, text, format) tuples to merge cells
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})
356
+
357
+ ### Conditional Formatting
358
+
359
+ Apply visual formatting based on cell values:
360
+
361
+ ```python
362
+ import xlsxturbo
363
+ import pandas as pd
364
+
365
+ df = pd.DataFrame({
366
+ 'name': ['Alice', 'Bob', 'Charlie', 'Diana'],
367
+ 'score': [95, 72, 88, 45],
368
+ 'progress': [0.9, 0.5, 0.75, 0.3],
369
+ 'status': [3, 2, 3, 1]
370
+ })
371
+
372
+ xlsxturbo.df_to_xlsx(df, "report.xlsx",
373
+ autofit=True,
374
+ conditional_formats={
375
+ # 2-color gradient: red (low) to green (high)
376
+ 'score': {
377
+ 'type': '2_color_scale',
378
+ 'min_color': '#FF6B6B',
379
+ 'max_color': '#51CF66'
380
+ },
381
+ # Data bars: in-cell bar chart
382
+ 'progress': {
383
+ 'type': 'data_bar',
384
+ 'bar_color': '#339AF0',
385
+ 'solid': True # Solid fill instead of gradient
386
+ },
387
+ # Icon set: traffic lights
388
+ 'status': {
389
+ 'type': 'icon_set',
390
+ 'icon_type': '3_traffic_lights'
391
+ }
392
+ }
393
+ )
394
+ ```
395
+
396
+ **Supported conditional format types:**
397
+
398
+ | Type | Options |
399
+ |------|---------|
400
+ | `2_color_scale` | `min_color`, `max_color` |
401
+ | `3_color_scale` | `min_color`, `mid_color`, `max_color` |
402
+ | `data_bar` | `bar_color`, `border_color`, `solid`, `direction` |
403
+ | `icon_set` | `icon_type`, `reverse`, `icons_only` |
404
+
405
+ **Available icon types:**
406
+ - 3 icons: `3_arrows`, `3_arrows_gray`, `3_flags`, `3_traffic_lights`, `3_traffic_lights_rimmed`, `3_signs`, `3_symbols`, `3_symbols_uncircled`
407
+ - 4 icons: `4_arrows`, `4_arrows_gray`, `4_traffic_lights`, `4_rating`
408
+ - 5 icons: `5_arrows`, `5_arrows_gray`, `5_quarters`, `5_rating`
409
+
410
+ Column patterns work with conditional formats:
411
+ ```python
412
+ # Apply data bars to all columns starting with "price_"
413
+ conditional_formats={'price_*': {'type': 'data_bar', 'bar_color': '#9B59B6'}}
414
+ ```
415
+
416
+ ### Formula Columns
417
+
418
+ Add calculated columns to your Excel output. Formulas are written after data columns and use `{row}` as a placeholder for the row number:
419
+
420
+ ```python
421
+ import xlsxturbo
422
+ import pandas as pd
423
+
424
+ df = pd.DataFrame({
425
+ 'price': [100, 200, 150],
426
+ 'quantity': [5, 3, 8],
427
+ 'tax_rate': [0.1, 0.1, 0.2]
428
+ })
429
+
430
+ xlsxturbo.df_to_xlsx(df, "sales.xlsx",
431
+ autofit=True,
432
+ formula_columns={
433
+ 'Subtotal': '=A{row}*B{row}', # price * quantity
434
+ 'Tax': '=D{row}*C{row}', # subtotal * tax_rate
435
+ 'Total': '=D{row}+E{row}' # subtotal + tax
436
+ }
437
+ )
438
+ ```
439
+
440
+ Formula columns appear after data columns (A=price, B=quantity, C=tax_rate, D=Subtotal, E=Tax, F=Total).
441
+
442
+ **Notes:**
443
+ - `{row}` is replaced with the Excel row number (1-based, starting at 2 for data rows when header=True)
444
+ - Formula columns inherit header formatting if specified
445
+ - Column order is preserved (first formula = first new column)
446
+ - Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
447
+
448
+ ### Merged Cells
449
+
450
+ Merge cell ranges to create headers, titles, or grouped labels:
451
+
452
+ ```python
453
+ import xlsxturbo
454
+ import pandas as pd
455
+
456
+ df = pd.DataFrame({
457
+ 'product': ['Widget A', 'Widget B'],
458
+ 'sales': [1500, 2300],
459
+ 'revenue': [7500, 11500]
460
+ })
461
+
462
+ # Merge cells for a title above the data
463
+ xlsxturbo.df_to_xlsx(df, "report.xlsx",
464
+ header=True,
465
+ merged_ranges=[
466
+ # Simple merge with text (auto-centered)
467
+ ('A1:C1', 'Q4 Sales Report'),
468
+ # Merge with custom formatting
469
+ ('A2:C2', 'Regional Data', {
470
+ 'bold': True,
471
+ 'bg_color': '#4F81BD',
472
+ 'font_color': 'white'
473
+ })
474
+ ]
475
+ )
476
+ ```
477
+
478
+ **Merged range format:**
479
+ - Tuple of `(range, text)` or `(range, text, format_dict)`
480
+ - Range uses Excel notation: `'A1:D1'`, `'B3:B10'`, etc.
481
+ - Format options same as `header_format`: bold, italic, font_color, bg_color, font_size, underline
482
+
483
+ **Notes:**
484
+ - Merged cells are applied after data is written, so plan row positions accordingly
485
+ - When using with `header=True`, data starts at row 2 (Excel row 2)
486
+ - Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
487
+
488
+ ### Hyperlinks
489
+
490
+ Add clickable links to cells:
491
+
492
+ ```python
493
+ import xlsxturbo
494
+ import pandas as pd
495
+
496
+ df = pd.DataFrame({
497
+ 'company': ['Anthropic', 'Google', 'Microsoft'],
498
+ 'product': ['Claude', 'Gemini', 'Copilot'],
499
+ })
500
+
501
+ # Add hyperlinks to a new column (D) after the data columns (A, B, C with header)
502
+ xlsxturbo.df_to_xlsx(df, "companies.xlsx",
503
+ autofit=True,
504
+ hyperlinks=[
505
+ # Header for the links column
506
+ ('C1', 'https://example.com', 'Website'),
507
+ # Links with company names as display text
508
+ ('C2', 'https://anthropic.com', 'anthropic.com'),
509
+ ('C3', 'https://google.com', 'google.com'),
510
+ ('C4', 'https://microsoft.com', 'microsoft.com'),
511
+ ]
512
+ )
513
+ ```
514
+
515
+ **Hyperlink format:**
516
+ - Tuple of `(cell, url)` or `(cell, url, display_text)`
517
+ - Cell uses Excel notation: `'A1'`, `'B5'`, etc.
518
+ - Display text is optional; if omitted, the URL is shown
519
+
520
+ **Notes:**
521
+ - Hyperlinks write to the specified cell position (overwrites existing content)
522
+ - To add a "links column", target cells beyond your DataFrame columns (as shown above)
523
+ - Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
524
+ - Not available in constant memory mode
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
340
712
 
341
713
  ### Constant Memory Mode (Large Files)
342
714
 
@@ -365,7 +737,18 @@ xlsxturbo.dfs_to_xlsx([
365
737
  - `table_style` (Excel tables)
366
738
  - `freeze_panes`
367
739
  - `row_heights`
740
+ - `conditional_formats`
741
+ - `merged_ranges`
742
+ - `hyperlinks`
743
+ - `comments`
744
+ - `validations`
745
+ - `rich_text`
746
+ - `images`
368
747
  - `autofit`
748
+ - `conditional_formats`
749
+ - `formula_columns`
750
+ - `merged_ranges`
751
+ - `hyperlinks`
369
752
 
370
753
  Column widths still work in constant memory mode.
371
754
 
@@ -424,15 +807,16 @@ xlsxturbo sales.csv report.xlsx -d eu -v --sheet-name "Q4 Sales"
424
807
 
425
808
  ## Performance
426
809
 
427
- 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 |
428
818
 
429
- | Method | Time | Speedup |
430
- |--------|------|---------|
431
- | **xlsxturbo** | 28.5s | **26.7x** |
432
- | PyExcelerate | 107s | 7.1x |
433
- | pandas + xlsxwriter | 374s | 2.0x |
434
- | pandas + openpyxl | 762s | 1.0x |
435
- | polars.write_excel | 1039s | 0.7x |
819
+ *Test system: Windows 11, Python 3.14, AMD Ryzen 9 (32 threads)*
436
820
 
437
821
  ## Type Detection Examples
438
822
 
@@ -468,14 +852,24 @@ maturin build --release
468
852
 
469
853
  ## Benchmarking
470
854
 
471
- Run the included benchmark script:
855
+ Run the included benchmark scripts:
472
856
 
473
857
  ```bash
474
- # Default: 100K rows x 50 columns
475
- 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
476
863
 
477
864
  # Custom size
478
- 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
479
873
  ```
480
874
 
481
875
  ## License