xlsxrb 0.1.15 → 0.1.17
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +125 -0
- data/README.md +24 -5
- data/benchmark/micro_ips.rb +70 -0
- data/lib/xlsxrb/chart_builder.rb +41 -2
- data/lib/xlsxrb/dsl_helpers.rb +522 -6
- data/lib/xlsxrb/elements/cell.rb +34 -9
- data/lib/xlsxrb/elements/column.rb +2 -2
- data/lib/xlsxrb/elements/styles.rb +427 -15
- data/lib/xlsxrb/elements/types.rb +1 -0
- data/lib/xlsxrb/elements/workbook.rb +79 -4
- data/lib/xlsxrb/elements/worksheet.rb +173 -11
- data/lib/xlsxrb/ooxml/cfb.rb +10 -0
- data/lib/xlsxrb/ooxml/reader/listeners/core_listeners.rb +77 -38
- data/lib/xlsxrb/ooxml/reader/listeners/drawing_listeners.rb +187 -24
- data/lib/xlsxrb/ooxml/reader/listeners/feature_listeners.rb +115 -2
- data/lib/xlsxrb/ooxml/reader/listeners/style_listeners.rb +28 -18
- data/lib/xlsxrb/ooxml/reader.rb +253 -136
- data/lib/xlsxrb/ooxml/shared_strings_parser.rb +83 -28
- data/lib/xlsxrb/ooxml/styles_parser.rb +76 -25
- data/lib/xlsxrb/ooxml/utils.rb +59 -2
- data/lib/xlsxrb/ooxml/workbook_parser.rb +57 -3
- data/lib/xlsxrb/ooxml/workbook_writer.rb +471 -86
- data/lib/xlsxrb/ooxml/worksheet_parser.rb +210 -127
- data/lib/xlsxrb/ooxml/worksheet_writer.rb +515 -184
- data/lib/xlsxrb/ooxml/writer/drawing_xml.rb +595 -453
- data/lib/xlsxrb/ooxml/writer/features_xml.rb +26 -9
- data/lib/xlsxrb/ooxml/writer/styles_xml.rb +87 -24
- data/lib/xlsxrb/ooxml/writer.rb +299 -59
- data/lib/xlsxrb/ooxml/xml_builder.rb +12 -4
- data/lib/xlsxrb/ooxml/xml_parser.rb +45 -0
- data/lib/xlsxrb/ooxml/zip_reader.rb +48 -29
- data/lib/xlsxrb/ooxml/zip_writer.rb +15 -5
- data/lib/xlsxrb/stream_row.rb +4 -3
- data/lib/xlsxrb/stream_sheet.rb +182 -8
- data/lib/xlsxrb/stream_writer.rb +977 -84
- data/lib/xlsxrb/style_builder.rb +59 -6
- data/lib/xlsxrb/utils.rb +80 -0
- data/lib/xlsxrb/version.rb +1 -1
- data/lib/xlsxrb/workbook_builder.rb +307 -24
- data/lib/xlsxrb/worksheet_builder.rb +1150 -49
- data/lib/xlsxrb.rb +705 -142
- data/sig/generated/xlsxrb/chart_builder.rbs +31 -3
- data/sig/generated/xlsxrb/dsl_helpers.rbs +119 -4
- data/sig/generated/xlsxrb/elements/cell.rbs +20 -3
- data/sig/generated/xlsxrb/elements/column.rbs +3 -3
- data/sig/generated/xlsxrb/elements/styles.rbs +271 -5
- data/sig/generated/xlsxrb/elements/types.rbs +2 -0
- data/sig/generated/xlsxrb/elements/workbook.rbs +63 -3
- data/sig/generated/xlsxrb/elements/worksheet.rbs +92 -2
- data/sig/generated/xlsxrb/ooxml/reader/listeners/core_listeners.rbs +2 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners/drawing_listeners.rbs +74 -2
- data/sig/generated/xlsxrb/ooxml/reader/listeners/feature_listeners.rbs +36 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners/style_listeners.rbs +5 -0
- data/sig/generated/xlsxrb/ooxml/reader.rbs +29 -0
- data/sig/generated/xlsxrb/ooxml/shared_strings_parser.rbs +3 -1
- data/sig/generated/xlsxrb/ooxml/styles_parser.rbs +5 -0
- data/sig/generated/xlsxrb/ooxml/utils.rbs +22 -0
- data/sig/generated/xlsxrb/ooxml/workbook_parser.rbs +15 -0
- data/sig/generated/xlsxrb/ooxml/workbook_writer.rbs +20 -4
- data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +19 -3
- data/sig/generated/xlsxrb/ooxml/worksheet_writer.rbs +15 -2
- data/sig/generated/xlsxrb/ooxml/writer/drawing_xml.rbs +16 -0
- data/sig/generated/xlsxrb/ooxml/writer/styles_xml.rbs +5 -2
- data/sig/generated/xlsxrb/ooxml/writer.rbs +42 -8
- data/sig/generated/xlsxrb/ooxml/xml_builder.rbs +4 -1
- data/sig/generated/xlsxrb/ooxml/zip_reader.rbs +6 -6
- data/sig/generated/xlsxrb/ooxml/zip_writer.rbs +8 -2
- data/sig/generated/xlsxrb/stream_row.rbs +3 -2
- data/sig/generated/xlsxrb/stream_sheet.rbs +122 -6
- data/sig/generated/xlsxrb/stream_writer.rbs +606 -30
- data/sig/generated/xlsxrb/style_builder.rbs +24 -6
- data/sig/generated/xlsxrb/utils.rbs +33 -0
- data/sig/generated/xlsxrb/workbook_builder.rbs +87 -5
- data/sig/generated/xlsxrb/worksheet_builder.rbs +557 -12
- data/sig/generated/xlsxrb.rbs +118 -20
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '021999337251e5676fd32d18d0f9f4f6bfd1379a7ee0d7caf8e1a4dba0ba4188'
|
|
4
|
+
data.tar.gz: e0985f7acd53f4d4945f2ff05d06d6e59595e683297f2d4701040947536169cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1c5689e894d657a94af63b2805b2cf24010bfc1de01b5794661b34c30d7252040da2a506b188f1cf9f3849878cd07b3187f7755bc1daa653161faf69c88d2be0
|
|
7
|
+
data.tar.gz: cefcef0cad4c356681ef2e3634dcd785a88dfde02beaee0b517411d7c79b4088a58647a8f5c89323ced13dbd7fbc4157e76caf6a50b32b40539e3ae6a68a8cb5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,130 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.1.17] - 2026-11-01
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Support threaded comments (`<ThreadedComments>`) and workbook person lists (`<personList>`) across `WorksheetBuilder`, `StreamWriter`, `WorksheetProxy`, `Ooxml::Writer`, `WorkbookWriter`, `DrawingXml`, `Elements::Worksheet`, `Elements::Cell`, `StreamSheet`, and `Ooxml::Reader`, serialize `xl/threadedComments/threadedComment{N}.xml` and `xl/persons/person.xml` conforming to the OpenXML threaded comments specification, support root comments and replies with `parent_id`, timestamps, and resolution states (`done`), register relationship types and content types, and provide query accessors across worksheets and cells.
|
|
7
|
+
- Support combo charts and secondary value axes across `ChartBuilder`, `DrawingXml`, `Ooxml::Writer`, and `DrawingListeners`, allow mixed chart types (e.g. bar and line) inside a single plot area with primary and secondary value axes (`secondary_value_axis:`, `secondary_axis:`), support per-series axis binding via `SeriesBuilder#secondary_axis` and `SeriesBuilder#axis`, serialize distinct `<c:valAx>` elements with independent scaling, orientation, units, and gridlines conforming to ECMA-376 Part 4 §21.2 schema sequence, and expose secondary value axis configurations in `Ooxml::Reader`.
|
|
8
|
+
- Support shared formula (`<f t="shared">`) serialization and reader expansion across `Xlsxrb.shared_formula`, `Xlsxrb.formula(..., shared_index:, si:)`, `WorksheetWriter`, `FeaturesXml`, `WorksheetParser`, and `WorksheetListener`, serialize master (`ref`, `si`) and follower (`si`) elements conforming to ECMA-376 Part 4 §18.3.1.40, and add `Xlsxrb::Utils.expand_formula` to expand relative cell references across shared formula ranges during SAX and streaming parsing.
|
|
9
|
+
- Support table calculated columns (`calculatedColumnFormula`) and custom totals row formulas (`totalsRowFormula`) across `TableBuilder`, `DslHelpers.normalize_table_options`, `WorkbookWriter`, `Writer`, and `FeatureListeners`, add `TableBuilder#calculated_column` helper, support `formula:`, `data_formula:`, `totals_row_formula:`, `total_row_formula:`, and `total_formula:` aliases, automatically infer `totalsRowFunction="custom"` when a totals formula is provided without an explicit function, and expose `calculated_column_formula` and `data_formula` in reader output.
|
|
10
|
+
- Support font text decoration attributes, including underline styles (`:single`, `:double`, `:single_accounting`, `:double_accounting`, `:none`) and vertical alignment (`:superscript`, `:subscript`, `:baseline`), with dedicated builder helpers (`superscript`, `subscript`, `vert_align`, `underline`) and option mappings across `StyleBuilder`, `WorkbookWriter`, `StylesXml`, `FeaturesXml`, `WorksheetWriter`, and `Ooxml::Reader`.
|
|
11
|
+
- Support row and column grouping DSL (`group_rows`, `group_columns`, `group_row`, `group_column`, `group_cols`, `group_col`) and outline view properties (`outline_properties`, `outline_pr`) across `WorksheetBuilder`, `StreamWriter`, `WorksheetProxy`, `Elements::Worksheet`, `StreamSheet`, and `Ooxml::Reader`, emit schema-compliant `<outlinePr applyStyles="..." summaryBelow="..." summaryRight="..." showOutlineSymbols="..."/>` in `WorksheetWriter`, and emit `collapsed="1"` on `<col>` elements.
|
|
12
|
+
- Enhance conditional formatting with statistical and relative rules (`above_average`, `top10`, `duplicate_values`, `unique_values`, `contains_blanks`, `not_contains_blanks`, `contains_errors`, `not_contains_errors`, `time_period`), add builder accessors (`above_average`, `equal_average`, `std_dev`, `rank`, `percent`, `bottom`, `text`, `time_period`) to `ConditionalFormatBuilder`, map symbol time periods (`:today`, `:last_7_days`, `:this_month`, etc.) to ECMA-376 `ST_TimePeriod` values, and automatically infer rule type from options.
|
|
13
|
+
- Add `ConditionalFormatBuilder` supporting block configuration for conditional formatting across `WorksheetBuilder`, `StreamWriter`, `WorksheetProxy`, and `Ooxml::Writer`.
|
|
14
|
+
- Add coordinate targeting (`columns:`, `rows:`, `col:`, `row:`, `except_rows:`) to `conditional_format` and `ConditionalFormatBuilder`, symmetrically resolving to `sqref`.
|
|
15
|
+
- Enhance `color_scale` conditional formatting with automatic cfvo generation for 2-color (`min`/`max`) and 3-color (`min`/50%/`max`) scales, and auto-normalize 6-digit RGB hex colors to 8-digit ARGB.
|
|
16
|
+
- Enhance `icon_set` conditional formatting with automatic cfvo percent distribution for 3, 4, and 5 icon sets, and support symbol icon set styles (`:three_arrows`, `:four_rating`, `:five_quarters`, etc.).
|
|
17
|
+
- Enhance `table` and `TableBuilder` with `auto_filter:` option, boolean `total_row` / `totals_row` helpers, symbol column names, and symbol `totals_row_function` mappings (`:sum`, `:average`, `:count`, `:min`, `:max`, `:count_nums`, `:std_dev`, `:var`, `:custom`).
|
|
18
|
+
- Add worksheet name sanitization utility via `Xlsxrb.sanitize_sheet_name` (aliased as `Xlsxrb.sheet_name` and `DslHelpers.sanitize_sheet_name`), sanitizing forbidden characters (`\`, `/`, `?`, `*`, `[`, `]`, `:`, control characters), replacing leading/trailing apostrophes, appending `replace_char` to the reserved `History` sheet name, and truncating names to 31 characters.
|
|
19
|
+
- Add `sheet_names` to `StreamWriter`, `WorksheetProxy`, and `WorkbookBuilder` returning an array of worksheet names currently registered in the workbook.
|
|
20
|
+
- Support `sheet.defined_name` symmetrically across `WorksheetBuilder`, `StreamWriter`, and `WorksheetProxy`, automatically quoting sheet names with single quote escaping and generating absolute `$` coordinate references.
|
|
21
|
+
- Support `fill: { color: ... }` and `fill_color:` in conditional formatting rules and `extract_dxf_from_rule!`.
|
|
22
|
+
- Support `size:` alongside `sz:` in conditional formatting differential formatting (`dxf`) fonts.
|
|
23
|
+
- Support `base_font:` in `Xlsxrb.rich_text(*runs, base_font: ...)` to merge base font attributes across text runs.
|
|
24
|
+
- Support `size:` alongside `sz:` in `RichTextRun`, inline rich text, and shared strings XML fonts.
|
|
25
|
+
- Add coordinate-based targeting (`columns:`, `rows:`, `except_rows:`, `col:`, `row:`) to `validate_data` and `DataValidationBuilder` with interval run compression (`compress_runs`), complement calculation (`complement_runs`), and automatic chunking into multiple `<dataValidation>` entries when reference counts exceed 32,767 (`Xlsxrb::MAX_REFS_PER_VALIDATION`).
|
|
26
|
+
- Expose spreadsheet dimension limits `Xlsxrb::MAX_ROW = 1_048_576`, `Xlsxrb::MAX_COL = 16_384`, and `Xlsxrb::MAX_REFS_PER_VALIDATION = 32_767`.
|
|
27
|
+
- Support direct array in `validate_data(..., in: [...])` converting to `type: "list"` with quoted CSV `formula1`.
|
|
28
|
+
- Automatically infer `show_input_message: true` when `prompt:` or `prompt_title:` is provided, infer `show_error_message: true` when `error:` or `error_title:` is provided, and convert multiline prompts and errors (`\r\n`, `\n`) to OOXML `ST_Xstring` format (`_x000a_`).
|
|
29
|
+
- Support explicit `:default` / `:none` cell style identifier to emit `s="0"` and prevent unintended column style inheritance.
|
|
30
|
+
- Merge duplicate `sheet.column` definitions on the same column index and sort by column index in in-memory and streaming modes.
|
|
31
|
+
- Inherit `sheet_format[:default_col_width]` when a column defines `style:` without explicit `width:`.
|
|
32
|
+
- Support sheet visibility states (`state:` `:visible`, `:hidden`, `:very_hidden`, and `hidden: true`) across `StreamWriter#sheet`, `WorksheetProxy`, and `WorkbookBuilder#sheet`, propagate state into streaming workbook XML, reject invalid state values with `ArgumentError`, and enforce that at least one visible worksheet exists in `strict_excel_mode`.
|
|
33
|
+
- Add active sheet and workbook view configuration (`active_sheet`, `first_sheet`, `show_sheet_tabs`, `tab_ratio`, and `sheet(..., active: true)`) across `WorkbookBuilder`, `StreamWriter`, and `WorksheetProxy`, resolve active tab indices safely to avoid hidden sheet selection, emit `activeTab`, `firstSheet`, `showSheetTabs`, and `tabRatio` on `<workbookView>`, guarantee single `tabSelected="1"` emission on the active sheet, and expose `active_tab`, `active_sheet`, `first_sheet`, `show_sheet_tabs?`, and `tab_ratio` on `Elements::Workbook`.
|
|
34
|
+
- Enhance worksheet tab color configuration (`tab_color`) across `WorksheetBuilder`, `StreamWriter`, and `WorksheetProxy` with support for RGB hex strings, theme colors, tint, indexed colors, and auto color, emit full `<tabColor>` attributes in `WorksheetWriter` and `Ooxml::Writer`, and expose `tab_color` readers on `Elements::Worksheet` and `StreamSheet`.
|
|
35
|
+
- Support worksheet error check warning suppression rules (`<ignoredErrors>`) via `ignore_error` and `add_ignored_error` across `WorksheetBuilder`, `StreamWriter`, and `WorksheetProxy`, emit schema-compliant `<ignoredErrors>` elements in `WorksheetWriter`, and expose `ignored_errors` readers on `Elements::Worksheet` and `StreamSheet` with full round-trip preservation.
|
|
36
|
+
- Support worksheet phonetic (furigana) guide properties (`<phoneticPr>`) via `phonetic_properties` (and alias `phonetic_pr`) across `WorksheetBuilder`, `StreamWriter`, and `WorksheetProxy`, emit schema-compliant `<phoneticPr>` elements in `WorksheetWriter` and `WorkbookWriter` in Part 4 schema sequence position, and expose `phonetic_properties` query readers on `Elements::Worksheet` and `StreamSheet` with full round-trip preservation.
|
|
37
|
+
- Support workbook and worksheet code names (`code_name`), page setup fit-to-page linkage (`fit_to_page`, `fit_to_page?`), and worksheet calculation properties (`sheet_calc_pr`, `full_calc_on_load?`) across `WorkbookBuilder`, `WorksheetBuilder`, `StreamWriter`, and `WorksheetProxy`, emit schema-compliant `codeName` on `<workbookPr>` and `<sheetPr>`, emit `<pageSetUpPr fitToPage="1"/>` and `<sheetCalcPr fullCalcOnLoad="1"/>` in `WorksheetWriter` conforming to ECMA-376 Part 4 schema sequence, and expose query methods and properties on `Elements::Workbook`, `Elements::Worksheet`, and `StreamSheet` with full round-trip fidelity.
|
|
38
|
+
- Support worksheet background image (`<picture r:id="..."/>`) via `background_image` (with aliases `background_picture`, `picture`, `background_image=`, `background_picture=`, `picture=`) across `WorksheetBuilder`, `StreamWriter`, and `WorksheetProxy`, serialize relationship in `xl/worksheets/_rels/sheet{N}.xml.rels` and binary media in `xl/media/sheet{N}_bg.{ext}`, register image extension in `[Content_Types].xml`, emit schema-compliant `<picture>` element in `WorksheetWriter` conforming to ECMA-376 Part 4 schema sequence, and expose `background_image` query readers on `Elements::Worksheet` and `StreamSheet` with full round-trip fidelity.
|
|
39
|
+
- Add visual regression test example, Visual Gallery section, and WebAssembly Live Playground preset for worksheet views and advanced properties (`sheet_properties`).
|
|
40
|
+
- Support hierarchical differential format Hash (`dxf: { font: ..., fill: ... }`) in `conditional_format` and `add_conditional_format` across `StreamWriter`, `WorksheetProxy`, `WorksheetBuilder`, and `Ooxml::Writer` with automatic deduplication, emit `<b val="0"/>`, `<i val="0"/>`, `<strike val="0"/>` on boolean `false` font properties, and support automatic/theme/tint font and background colors and `pattern: "none"` pattern fills.
|
|
41
|
+
- Add `skip_rows` (and alias `skip_row`) to `StreamWriter`, `WorksheetProxy`, and `WorksheetBuilder` to advance the row cursor without writing empty `<row>` XML elements.
|
|
42
|
+
- Support configurable workbook-wide default font (`default_font: { name:, size: }`, `default_font(name, size, **opts)`, `set_default_font`) across `StreamWriter`, `WorksheetProxy`, `WorkbookBuilder`, `Xlsxrb.write`, and `Xlsxrb.build`, serializing name, size, family, charset, and scheme onto the base font (font 0).
|
|
43
|
+
- Support inline strings option (`shared_strings: false`) across `StreamWriter` and `Xlsxrb.write`, writing cells directly as `<c t="inlineStr"><is><t>...</t></is></c>` and omitting `xl/sharedStrings.xml` and package relationships to maintain flat $O(1)$ constant memory during streaming write.
|
|
44
|
+
- Support configurable ZIP deflate compression level (`compression_level: 0..9`) across `StreamWriter`, `Ooxml::ZipWriter`, `Ooxml::WorkbookWriter`, and `Xlsxrb.write`, allowing performance tuning between compression speed (e.g. level 4 for 20-40% faster writes) and archive size.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
- Unify differential format extraction across `WorkbookWriter` and `StreamWriter` into centralized `DslHelpers.extract_dxf_from_rule!`.
|
|
48
|
+
- Synchronize mutation testing metrics in `README.md`, `docs/MUTATION_TESTING.md`, and `docs/QUALITY_ASSURANCE.md` to reflect 51 pure functional subjects and 2,549 mutations at 100.00% kill rate.
|
|
49
|
+
- Optimize `bin/pre-push` and test suite execution time:
|
|
50
|
+
- Scope mutation testing (`rake mutant:pure`) to subjects and test files matching changed files via `MUTANT_FILES` (running only affected pure logic subjects in ~2-20s rather than all 51 subjects), falling back to full coverage when `Rakefile` or `.mutant.yml` changes or when `--force` is passed, and suppressing parser future-frozen string warnings during mutation runs.
|
|
51
|
+
- Eliminate duplicate `rake sig` invocation in `bin/pre-push` (already invoked as a prerequisite of `rake typecheck`) and run unit and contract tests in a single `rake test` process rather than two separate test processes.
|
|
52
|
+
- Decouple serial RBS runtime type validation (`test:rbs`) from default local `bin/pre-push --all` (saving ~160s locally where static Steep type checking already ensures zero errors), while preserving automated runtime type validation on CI and allowing manual execution with `bin/pre-push --all --rbs` or `RUN_RBS=1`.
|
|
53
|
+
- Condition SimpleCov coverage profiling on `ENV["COVERAGE"] == "1"` or `ENV["CI"] == "true"`, eliminating 5-10s of instrumentation overhead during local test runs.
|
|
54
|
+
- Replace anonymous keyword argument forwarding (`**`) in `StreamWriter` and `WorksheetBuilder` with explicit `**opts` to prevent Steep 2.1.0 internal `RuntimeError` crashes during type synthesis.
|
|
55
|
+
- Update `bin/setup` to install `.git/hooks/pre-push` as a wrapper forwarding directly to `bin/pre-push --all`, ensuring `git push` runs extended diff-scoped mutation verification while staying synchronized with `bin/pre-push`.
|
|
56
|
+
|
|
57
|
+
### Fixed
|
|
58
|
+
- Guard `Ooxml::Writer#generate_table_xml` against `TypeError` when `tbl[:style]` is provided as a string, and resolve table style attributes (`show_first_column`, `show_last_column`, `show_row_stripes`, `show_column_stripes`) directly from table definition root.
|
|
59
|
+
- Omit `<autoFilter>` tag from table XML when `auto_filter: false` is explicitly specified across `Ooxml::Writer` and `WorkbookWriter`.
|
|
60
|
+
- Enforce strict Excel worksheet name rules in `StreamWriter#sheet`, `WorkbookBuilder#sheet`, and `Elements::Worksheet.valid_name?` / `validate`, rejecting colons (`:`), leading/trailing apostrophes (`'`), and the case-insensitive reserved name `History`, updating validation error messages to reference Excel constraints.
|
|
61
|
+
- Guard against non-finite Float values (`Float::INFINITY`, `Float::NAN`, and non-finite cached formula values) in `StreamWriter#row` and `WorksheetWriter#write_row_values`, raising `ArgumentError` to prevent corrupt XML output.
|
|
62
|
+
- Support `xsd:boolean` representations (`"true"`, `"false"`, `"1"`, `"0"`) across `StylesParser`, `StylesListener`, and column definitions in `WorksheetParser`.
|
|
63
|
+
- Safely handle `nil` shared strings table index (`sst_index: nil`) across unstyled fast path and general cell paths in `WorksheetWriter#write_row_values`, writing string values as inline strings (`t="inlineStr"`) without error.
|
|
64
|
+
- Include `test/ooxml/**/*_test.rb` in Rake test tasks and update custom number format assertions to use `Elements::Styles#num_fmt`.
|
|
65
|
+
- Preserve formatted text runs (`<r><rPr>...</rPr><t>...</t></r>`) in inline strings (`<c t="inlineStr">`) for `Elements::RichText` cells across unstyled fast path and general row paths in `WorksheetWriter#write_row_values` when `shared_strings: false` is used.
|
|
66
|
+
|
|
67
|
+
## [0.1.16] - 2026-10-24
|
|
68
|
+
|
|
69
|
+
### Changed
|
|
70
|
+
- Encapsulate `Elements::Styles` with dedicated collection readers (`fonts`, `fills`, `borders`, `cell_xfs`, `num_fmts`, `dxfs`, `cell_style_xfs`, `cell_styles`, `table_styles`, `indexed_colors`, `mru_colors`, `colors`) and item lookups (`font`, `fill`, `border`, `cell_xf` / `xf`, `num_fmt`, `dxf`, `cell_style_xf`, `cell_style`), removing public Hash delegation (`[]`, `[]=`, `fetch`, `dig`, `keys`, `values`, `each`, `merge!`), and provide builder helpers (`add_font`, `add_fill`, `add_border`, `add_num_fmt`, `add_xf`, `add_dxf`, `add_cell_style_xf`, `add_cell_style`, `add_style`) that append entries, invalidate memoized caches, and return allocated IDs.
|
|
71
|
+
- Enforce binary encoding (`ASCII-8BIT`) on raw XML buffers in `StreamRow` and document byte vs character indexing consistency policy in `AGENTS.md` to prevent terminal row truncation or byte-offset drift when scanning documents with multibyte UTF-8 characters.
|
|
72
|
+
- Clarify streaming write memory profile and date/time timezone semantics in README and YARD documentation.
|
|
73
|
+
- Reading a file that is not a ZIP archive (including an empty file) raises `Xlsxrb::ZipError` instead of `ArgumentError`.
|
|
74
|
+
- Strict numeric cell values: an explicit numeric cell (`t="n"`) with an invalid number raises `Xlsxrb::ParseError`, while an empty `<v>` reads as `nil` instead of `0`, exponent notation such as `1E-3` reads as a Float, and non-numeric values in cells without `t` fall back to their string payload. Inline string and formula string cells without text read as `nil`, and `t="d"` cells read as their ISO 8601 text.
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
- Compound File Binary (CFB) header validation: Validate major version (3 or 4) and sector shifts in `Ooxml::Cfb::Reader#parse_header` and bounds-check sector reading against file size in `read_sector`, preventing `RangeError` integer overflows on corrupted or tampered inputs and raising `Xlsxrb::Error` gracefully.
|
|
78
|
+
- Unstyled empty cells and column ranges: Suppress style attribute (`s="..."`) emission on unstyled empty cells in `WorksheetWriter#write_row`, `write_cell`, and `write_row_values` to prevent unintended style inheritance explosion when rows or columns have styles, and preserve column `min`..`max` ranges on round-trip across `build_worksheet`, `Elements::Column`, and `WorksheetWriter#write_columns`.
|
|
79
|
+
- Data validation `showDropDown` and `imeMode` attributes: Serialize `showDropDown="1"` and `imeMode` on `<dataValidation>` elements in `WorksheetWriter#write_data_validations`, matching `Ooxml::Writer` and `DataValidationsListener`.
|
|
80
|
+
- Sheet views and selection serialization: Omit hardcoded `tabSelected="1"` in `WorksheetWriter#write_sheet_views` unless `tab_selected` is true, preventing multiple sheets from opening simultaneously selected in Excel, serialize full sheet view properties (`top_left_cell`, `zoom_scale_normal`, `view`, etc.), and preserve active cell selection across `WorksheetWriter`, `Elements::Worksheet`, `StreamSheet`, and `Xlsxrb.write`.
|
|
81
|
+
- Differential format (`<dxfs>`) parsing and boolean font properties: Parse differential formats (`<dxf>`) in `Ooxml::StylesParser` and serialize font, `numFmt`, fill, alignment, border, and protection in `WorkbookWriter`, parse and serialize ECMA-376 `CT_BooleanProperty` attributes (`<b val="0"/>`, `<i val="0"/>`) for font properties, and preserve `<color auto="1"/>` in `StylesParser`.
|
|
82
|
+
- Column and row style inheritance and serialization: Resolve named and inline column and row styles to integer style indices and propagate style inheritance to unstyled cells in `WorkbookBuilder#process_styles`, serialize row style attribute `s` on streaming `<row>` elements in `WorksheetWriter#write_row_values`, and guard `<col style>` emission against non-integer identifiers.
|
|
83
|
+
- Resolve boolean (`t="b"`) formula `cached_value` payloads (`"1"`, `"0"`, `"true"`, `"false"`) to `true` and `false` in `Ooxml::Reader::WorksheetListener#store_cell_value`, matching streaming and DOM cell parsers.
|
|
84
|
+
- Forward `:type`, `:ref`, and `:shared_index` (`:si`) formula attributes when building cells from a formula Hash (`{ formula: ..., type: :array, ref: ... }`) in `WorksheetBuilder#row`, `Xlsxrb.build_raw_cell`, and `Xlsxrb.build_raw_cell_from_value`.
|
|
85
|
+
- Unify `Ooxml::Reader#extract_zip_entry` and `Ooxml::Reader#entry_names` with `Ooxml::ZipReader` to support case-insensitive OPC part name resolution (per ECMA-376 Part 2 §9.1.1.2), ZIP64 archives, and data-descriptor streams uniformly across all reader APIs.
|
|
86
|
+
- Exclude non-worksheet relationship parts (`chartsheet`, `dialogsheet`, `macrosheet`, `xlMacrosheet`, `xlIntlMacrosheet`) and `xl/chartsheets/`, `xl/dialogsheets/`, `xl/macrosheets/` targets from standard worksheet enumeration in `Xlsxrb.read`, `Xlsxrb.modify`, and `Ooxml::Reader#discover_sheets`.
|
|
87
|
+
- Excel date boundary guards: Raise `ArgumentError` when writing dates before the date system epoch (`1899-12-31` / `1900-01-01` in 1900 date system or `1904-01-01` in 1904 date system) or after `9999-12-31` in `date_to_serial`, and raise `ArgumentError` when reading negative serials or serials exceeding `9999-12-31` (`serial >= 2_958_466` in 1900 system or `serial >= 2_957_004` in 1904 system) in `serial_to_date`, adhering to ECMA-376 date constraints and preventing corrupted Julian day calculations or negative date representation errors.
|
|
88
|
+
- Formula cached value falsy resolution and boolean typing: Fix falsy checks on formula `cached_value` across `Xlsxrb`, `build_raw_cell_from_value`, and `WorksheetWriter` so that boolean `false` and numeric `0` are not skipped or converted to `nil`, and serialize boolean cached values with cell type `t="b"` and `<v>1</v>` / `<v>0</v>` per ECMA-376 Part 4 §18.3.1.4.
|
|
89
|
+
- Boolean cell parsing tolerance: Support text payloads `"true"` and `"false"` (case-insensitive) in addition to numeric `"1"` and `"0"` for cells with boolean type attribute `t="b"`, conforming to ECMA-376 `xs:boolean` specification and preventing boolean `true` cells from evaluating to `false`.
|
|
90
|
+
- Template modification in `Xlsxrb.modify`: Support case-insensitive and slash-normalized archive entry replacement per ECMA-376 Part 2 §7.3, preventing shadowed or duplicate entries when archives use mixed casing (such as `xl/SharedStrings.xml`) or Windows backslashes (such as `xl\worksheets\sheet1.xml`). Preserve XML namespace prefixes (such as `<x:worksheet>`, `<x:sst>`, `<x:si>`) during sheetData replacement and shared strings appending, prevent duplicate `xmlns:r` attribute definitions in namespaced and ISO/IEC 29500 Strict documents, and correctly anchor `<hyperlinks>` and `<legacyDrawing>` after preceding root container elements to avoid illegal nesting inside `<customSheetViews>` or extension blocks.
|
|
91
|
+
- Resolve Open Packaging Conventions (OPC) workbook part path dynamically via root relationships (`_rels/.rels`), enabling reading and modifying workbooks with non-standard part names or root locations (such as `xl/workbook2.xml` or `workbook.xml`), and fall back to standard sheet and part locations when workbook relationships are missing or corrupted.
|
|
92
|
+
- Handle multibyte UTF-8 characters encoded as ASCII-8BIT (binary) strings during XML escaping and buffer concatenation in worksheet writer, workbook writer, and streaming sheet, preventing `Encoding::CompatibilityError` when cell values or formulas contain multibyte text.
|
|
93
|
+
- ECMA-376 schema conformance during workbook serialization: Format hash-based colors (`{ theme: ... }`, `{ indexed: ... }`, `{ auto: ... }`) into schema-compliant `<color>` elements in stylesheets and workbooks instead of emitting unparseable Hash string representations (`rgb="{...}"`), strictly validate 6/8-character hex colors against `ST_UnsignedIntHex`, reorder DrawingML line and character properties (`a:ln`, `a:rPr`) to satisfy ECMA-376 `xs:sequence` child constraints, preserve leading and trailing whitespace using `xml:space="preserve"` on `<si><t>` elements in `sharedStrings.xml`, drop data validations missing required `sqref` references, and omit negative `firstPageNumber` values in worksheet `page_setup`.
|
|
94
|
+
- String fallback when cell type attribute `t` is omitted: Parse numeric strings (integers, floats, exponents) when cell type attribute is omitted, while treating non-numeric text payloads in `<v>` as raw strings rather than raising `Xlsxrb::ParseError`, allowing parsing of cells generated by Open XML SDK and other spreadsheet producers without crashing.
|
|
95
|
+
- XML namespace prefix tolerance in shared strings parser (`Ooxml::SharedStringsParser`): Locate `<sst>`, `<si>`, `<t>`, and `<rPh>` tags dynamically regardless of XML namespace prefixes (such as `<x:sst>`, `<x:si>`, `<x:t>`), preventing dropped shared strings in spreadsheets produced by Microsoft Open XML SDK, ClosedXML, and other XML-prefixed generators.
|
|
96
|
+
- Consolidate cell value interpretation across streaming fast-scan direct, row values, events, and SAX listener parsers into a centralized `Ooxml::WorksheetParser.resolve_cell_value` helper, eliminating redundant logic and preventing parser divergence.
|
|
97
|
+
- Preserve worksheet and workbook attributes during in-memory writes (`Xlsxrb.write(workbook)`): accurately map and serialize `freeze_pane`, `split_pane`, `sheet_view`, `page_setup`, `page_margins`, `header_footer`, `print_options`, `sheet_protection`, `tables`, `merged_cells`, `auto_filter`, `row_breaks`, `col_breaks`, and embedded images from `Elements::Worksheet`, and propagate workbook properties (`core_properties`, `app_properties`, `custom_properties`, `workbook_protection`).
|
|
98
|
+
- Support arbitrary XML attribute ordering on cells (such as `t` before `s` or `r` after `s`) in streaming fast-scan parsers, ensuring cell types and styles are never dropped regardless of producer attribute sequence.
|
|
99
|
+
- Empty self-closing string cells without `<v>` in fast scan parsers evaluate to `nil` instead of empty string `""`.
|
|
100
|
+
- In-memory SAX listeners (`WorksheetListener` and `SharedStringsListener`) skip phonetic readings (`<rPh>`) and treat empty self-closing string cells (`<c ... t="s"/>`) as nil, matching streaming reader behavior.
|
|
101
|
+
- Rich text parsing in shared strings parser (`Ooxml::SharedStringsParser`): Handle self-closing `<t/>` tags when the first run is empty without dropping subsequent runs, while bounding phonetic tag scans to maintain linear throughput.
|
|
102
|
+
- `extract_inline_text` and `extract_multi_t` strictly distinguish `<t>` elements from other XML tags containing 't' (such as `<rPr>`, `<vertAlign>`, or `<table>`), preventing corrupted inline string parsing in rich text runs.
|
|
103
|
+
- Formulas are serialized strictly when explicitly defined via `Xlsxrb.formula(...)` or `{ formula: ... }`. Plain string cells starting with `=` are always serialized as text cells, preventing formula injection vulnerabilities and unintended 0-value coercions on reload.
|
|
104
|
+
- Streaming reads no longer drop the cell that follows a self-closing empty cell (`<c r="A1" s="1"/>`) in `StreamRow#values` and `StreamRow#cells`.
|
|
105
|
+
- Streaming reads place a row-leading cell with a multi-letter column reference (`AB1`) in its own column instead of column A.
|
|
106
|
+
- Phonetic readings (`<rPh>`) are excluded from shared strings and inline strings, so cells with furigana no longer concatenate phonetic readings to display text.
|
|
107
|
+
|
|
108
|
+
### Added
|
|
109
|
+
- Extract and preserve `activeTab` (`<workbookView>`) and `fullCalcOnLoad` (`<calcPr>`) in `Ooxml::WorkbookParser`, `Xlsxrb.read`, and `Xlsxrb.modify`, and expose `workbook_properties`, `active_tab`, and `full_calc_on_load?` (`full_calc_on_load`) readers on `Elements::Workbook`.
|
|
110
|
+
- Support self-closing row tags (`<row r="N" .../>`): Extract row indices and custom row attributes (`ht`, `hidden`, `customHeight`, `outlineLevel`, `collapsed`, `s`) and emit `:row_start` / `:row_end` events and empty `StreamRow` instances across direct and streaming worksheet parsers.
|
|
111
|
+
- ISO/IEC 29500-1 ST_Xstring control character decoding: Support decoding `_xHHHH_` hexadecimal escape sequences (such as `_x000D_` carriage returns and `_x005F_` literal underscores) in `XmlBuilder.unescape`, `SharedStringsParser`, and `SharedStringsListener`, correctly normalizing newlines and preserving control character fidelity in cell strings.
|
|
112
|
+
- Record Architecture Decision Record [ADR 0001: Excel Date System Boundary Handling](docs/adr/0001-excel-date-system-boundary-handling.md) documenting date conversion semantics, ecosystem survey, and design rationale across 1900 and 1904 date systems.
|
|
113
|
+
- Date validation and boundary checks: Add `Xlsxrb::Ooxml::Utils.valid_excel_date?` (and delegates on `Xlsxrb::Utils` and `Xlsxrb`) to check if a numeric serial value falls within the supported Excel date boundaries [0, 2,958,465] (1900 system) or [0, 2,957,003] (1904 system).
|
|
114
|
+
- `ZipReader#open_entry_io` and `ZipReader#each_entry_chunk` support an `encoding:` parameter and automatically resolve `.xml` and `.rels` entries to UTF-8 encoding, preventing `Encoding::CompatibilityError` in downstream text and regular expression operations.
|
|
115
|
+
- `Xlsxrb::Ooxml::Utils.booleanize` and `Xlsxrb::Ooxml::Utils.camelize` helper methods for OOXML attribute serialization and name casing conversion.
|
|
116
|
+
- Round-trip contract tests in `test/contract/round_trip_fidelity_test.rb` verifying that `read -> write -> read` preserves worksheet and workbook attributes across streaming and loaded in-memory representations.
|
|
117
|
+
- Alias `conditional_formatting` to `conditional_formats` on `StreamSheet` for complete API symmetry with `Elements::Worksheet`.
|
|
118
|
+
- Property-based tests for cell XML parsing in `test/pbt/cell_xml_parser_pbt_test.rb`, validating 100% equivalence between streaming fast-scan and SAX listener parsers across random attribute permutations, namespaces, rich text, and phonetic elements.
|
|
119
|
+
- Dedicated exception `Xlsxrb::ZipBombError < Xlsxrb::ZipError` for archive expansion limit violations.
|
|
120
|
+
- Workbook view and calculation properties in in-memory writes: `Xlsxrb.write(workbook)` emits `<bookViews><workbookView activeTab="n"/></bookViews>` and `<calcPr fullCalcOnLoad="1"/>` when the workbook properties carry `active_tab:` and `full_calc_on_load: true`.
|
|
121
|
+
- Autonomous QA agent squad skill definitions and documentation: Added skill playbooks in `.agents/skills/` (`throughput-performance`, `security-resilience`, `api-contract`, `concurrency-safety`, `visual-fidelity`), and updated `docs/QA_AGENTS.md`, `docs/QUALITY_ASSURANCE.md`, `.agents/rules/qa_agents.md`, and `AGENTS.md`.
|
|
122
|
+
- Visual fidelity and Wasm compatibility verification agent (`visual-fidelity`): Formulated autonomous skill playbook covering headless LibreOffice Calc visual regression testing (`rake test:visual`) and WebAssembly playground asset verification (`rake doc:check`).
|
|
123
|
+
- API symmetry and contract verification agent (`api-contract`): Added reflective method signature and parameter arity parity tests between `WorksheetBuilder`, `StreamWriter`, and `WorksheetProxy` in `test/contract/api_symmetry_test.rb`, added `very_hidden?` predicate on both `Elements::Worksheet` and `StreamSheet`, and enforced lossless round-trip serialization contracts between streaming and in-memory workflows.
|
|
124
|
+
- Security and resilience verification agent (`security-resilience`): Added regular expression ReDoS audit tests in `test/security_resilience_test.rb` verifying linear processing under 10k+ character adversarial inputs, supported `max_uncompressed_size` option on `Xlsxrb.read` block form to protect streaming decompression from ZIP bombs, and added `escape_formulas: true` option on `Xlsxrb.write`, `StreamWriter`, and `WorksheetWriter` to prevent formula and CSV injection vulnerabilities.
|
|
125
|
+
- Throughput and complexity verification agent (`throughput-performance`): Added deterministic allocation bounds testing (< 25 objects/row write, < 15 objects/row read for 10k rows) and scaling ratio tests in `test/performance/throughput_test.rb`, hot-path microbenchmarks with `benchmark-ips` (`benchmark/micro_ips.rb`), and corresponding `benchmark:ips` rake task.
|
|
126
|
+
- Concurrency and Ractor safety verification agent (`concurrency-safety`): Added automated reflection scan testing that 100% of constants in the `Xlsxrb` namespace are `Ractor.shareable?`, deeply froze `EPOCH_1900`, `EPOCH_1904`, `INTEGER_STRINGS`, and `LOCAL_HEADER_SIG`, added Ractor-safe accessors for REXML security settings in `XmlParser`, and added full parallel write/read/modify pipeline verification across concurrent Ractors in `test/concurrent_test.rb`.
|
|
127
|
+
|
|
3
128
|
## [0.1.15] - 2026-10-17
|
|
4
129
|
|
|
5
130
|
### Added
|
data/README.md
CHANGED
|
@@ -23,7 +23,7 @@ The Ruby ecosystem has several XLSX libraries designed for specific tradeoffs:
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
These libraries make different architectural tradeoffs:
|
|
26
|
-
* Streaming Model: Writes or reads rows sequentially on-the-fly to maintain a
|
|
26
|
+
* Streaming Model: Writes or reads rows sequentially on-the-fly to maintain a low-memory footprint regardless of dataset size.
|
|
27
27
|
* In-Memory Model: Builds a complete document object model, offering flexible random access, cell updates, and document templates at the cost of memory usage on large spreadsheets.
|
|
28
28
|
|
|
29
29
|
Maintaining a gem that supports both reading and writing across streaming and in-memory models, alongside OOXML features and specification compatibility, involves significant ongoing maintenance overhead.
|
|
@@ -33,14 +33,14 @@ Maintaining a gem that supports both reading and writing across streaming and in
|
|
|
33
33
|
### Design Principles
|
|
34
34
|
|
|
35
35
|
- Zero Runtime Dependencies: Built strictly on the Ruby standard library and bundled gems (`zlib`, `rexml`, etc.) with zero third-party runtime dependencies.
|
|
36
|
-
- Streaming Support:
|
|
36
|
+
- Streaming Support: Low-memory row streaming for reading and writing spreadsheets ($O(1)$ memory on reads; writes buffer unique shared strings).
|
|
37
37
|
- OpenXML Interoperability: Compliant with ISO/IEC 29500 (ECMA-376) and validated against the Microsoft [Open XML SDK](https://github.com/dotnet/Open-XML-SDK).
|
|
38
38
|
- AI-Assisted Maintenance: Uses AI coding agents for automated quality assurance and verification workflows. Operational guidelines are defined in [AGENTS.md](AGENTS.md).
|
|
39
39
|
- Ruby 4.0+: Requires Ruby 4.0 or higher.
|
|
40
40
|
|
|
41
41
|
### Autonomous Quality Assurance
|
|
42
42
|
|
|
43
|
-
To maintain its multi-layered QA suite (Steep static typing, 100% mutation kill rate across
|
|
43
|
+
To maintain its multi-layered QA suite (Steep static typing, 100% mutation kill rate across 51 subjects, ECMA-376 XSD validation, and $O(1)$ streaming memory) without high manual maintenance overhead, xlsxrb defines remediation workflows for AI coding agents ([AGENTS.md](AGENTS.md), [docs/QA_AGENTS.md](docs/QA_AGENTS.md)).
|
|
44
44
|
|
|
45
45
|
## Installation
|
|
46
46
|
|
|
@@ -65,7 +65,7 @@ You can also browse 80+ rendered visual examples across all features in the [Vis
|
|
|
65
65
|
|
|
66
66
|
### Quick Start: Streaming (Recommended for Large Files)
|
|
67
67
|
|
|
68
|
-
#### Streaming Write (
|
|
68
|
+
#### Streaming Write (Low Memory)
|
|
69
69
|
```ruby
|
|
70
70
|
require "xlsxrb"
|
|
71
71
|
|
|
@@ -78,6 +78,9 @@ Xlsxrb.write("large_output.xlsx") do |writer|
|
|
|
78
78
|
end
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
> [!NOTE]
|
|
82
|
+
> Streaming writes pipe row and cell XML directly to the archive without buffering rows, keeping cell data memory constant. Because shared strings are indexed across the workbook to minimize output file size, memory scales with the count of distinct strings.
|
|
83
|
+
|
|
81
84
|
#### Streaming Read ($O(1)$ Memory)
|
|
82
85
|
```ruby
|
|
83
86
|
require "xlsxrb"
|
|
@@ -139,6 +142,22 @@ Xlsxrb.read("confidential.xlsx", password: "SecretPassword123") do |sheet|
|
|
|
139
142
|
end
|
|
140
143
|
```
|
|
141
144
|
|
|
145
|
+
### Date and Time Handling
|
|
146
|
+
|
|
147
|
+
Excel stores dates and times as floating-point serial numbers without timezone information. When reading spreadsheet files, `xlsxrb` returns date-formatted cells as UTC `Time` objects (`Time.utc`).
|
|
148
|
+
|
|
149
|
+
To interpret an Excel timestamp in a specific application timezone (such as JST or America/New_York), construct a local time using the year, month, day, hour, min, and sec components:
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
# With ActiveSupport / Rails:
|
|
153
|
+
local_time = Time.zone.local(cell.value.year, cell.value.month, cell.value.day,
|
|
154
|
+
cell.value.hour, cell.value.min, cell.value.sec)
|
|
155
|
+
|
|
156
|
+
# With Ruby standard library:
|
|
157
|
+
local_time = Time.local(cell.value.year, cell.value.month, cell.value.day,
|
|
158
|
+
cell.value.hour, cell.value.min, cell.value.sec)
|
|
159
|
+
```
|
|
160
|
+
|
|
142
161
|
### IDE Autocompletion & Ruby LSP Support
|
|
143
162
|
|
|
144
163
|
Includes a native Ruby LSP Add-on and full RBS signatures for zero-configuration method autocompletion and hover documentation in VS Code and other editors:
|
|
@@ -173,7 +192,7 @@ Verified by a multi-layered QA architecture:
|
|
|
173
192
|
* Microsoft Open XML SDK Validation: Validates generated OOXML structures against Microsoft's official SDK.
|
|
174
193
|
* Visual Regression Testing (VRT): Headless LibreOffice Calc pixel-by-pixel rendering checks.
|
|
175
194
|
* Contract & Round-Trip Tests: Verifies parity between Streaming and In-Memory APIs and round-trip read/write accuracy.
|
|
176
|
-
* Mutation Testing (Mutant): 100% mutant kill rate (
|
|
195
|
+
* Mutation Testing (Mutant): 100% mutant kill rate (2,549/2,549 mutations across 51 subjects) on pure algorithms, predicates, and coordinates ([docs/MUTATION_TESTING.md](docs/MUTATION_TESTING.md)).
|
|
177
196
|
* Security & DoS Protection: Formula injection mitigation and ZIP bomb protection.
|
|
178
197
|
|
|
179
198
|
For full architectural details, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) and [docs/QUALITY_ASSURANCE.md](docs/QUALITY_ASSURANCE.md).
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "benchmark/ips"
|
|
4
|
+
require_relative "../lib/xlsxrb"
|
|
5
|
+
|
|
6
|
+
puts "=" * 70
|
|
7
|
+
puts "xlsxrb Hot-Path Microbenchmarks (benchmark-ips)"
|
|
8
|
+
puts "Ruby: #{RUBY_DESCRIPTION}"
|
|
9
|
+
puts "=" * 70
|
|
10
|
+
|
|
11
|
+
ips_time = (ENV["IPS_TIME"] || "2").to_f
|
|
12
|
+
ips_warmup = (ENV["IPS_WARMUP"] || "1").to_f
|
|
13
|
+
|
|
14
|
+
# 1. XML escaping hot path
|
|
15
|
+
sample_escape_string = "Customer & Company <sales@example.com> \"Q1 '2026'\""
|
|
16
|
+
puts "\n--- 1. XmlBuilder.escape ---"
|
|
17
|
+
Benchmark.ips do |x|
|
|
18
|
+
x.config(time: ips_time, warmup: ips_warmup)
|
|
19
|
+
x.report("XmlBuilder.escape") do
|
|
20
|
+
Xlsxrb::Ooxml::XmlBuilder.escape(sample_escape_string)
|
|
21
|
+
end
|
|
22
|
+
x.compare!
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# 2. Coordinate conversion hot path
|
|
26
|
+
puts "\n--- 2. Cell.calculate_column_index ---"
|
|
27
|
+
Benchmark.ips do |x|
|
|
28
|
+
x.config(time: ips_time, warmup: ips_warmup)
|
|
29
|
+
x.report("Cell.calculate_column_index ('XFD1048576')") do
|
|
30
|
+
Xlsxrb::Elements::Cell.calculate_column_index("XFD1048576")
|
|
31
|
+
end
|
|
32
|
+
x.report("Cell.calculate_column_letter (16384)") do
|
|
33
|
+
Xlsxrb::Elements::Cell.calculate_column_letter(16_384)
|
|
34
|
+
end
|
|
35
|
+
x.compare!
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# 3. Number formatting hot path
|
|
39
|
+
sample_numbers = [1234.5678, -42.0, 0.05, 1_000_000.99]
|
|
40
|
+
sample_format_code = "#,##0.00;(#,##0.00);\"-\""
|
|
41
|
+
puts "\n--- 3. NumberFormatter.format ---"
|
|
42
|
+
Benchmark.ips do |x|
|
|
43
|
+
x.config(time: ips_time, warmup: ips_warmup)
|
|
44
|
+
x.report("NumberFormatter.format (custom pattern)") do
|
|
45
|
+
sample_numbers.each do |num|
|
|
46
|
+
Xlsxrb::NumberFormatter.format(num, sample_format_code)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
x.report("NumberFormatter.format (currency)") do
|
|
50
|
+
sample_numbers.each do |num|
|
|
51
|
+
Xlsxrb::NumberFormatter.format(num, "$#,##0.00")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
x.compare!
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# 4. WorksheetParser row scanning hot path
|
|
58
|
+
sample_row_xml = '<row r="1" spans="1:4"><c r="A1" t="s"><v>0</v></c><c r="B1"><v>123.45</v></c><c r="C1" s="1"><v>44927</v></c><c r="D1" t="inlineStr"><is><t>Inline text</t></is></c></row>'
|
|
59
|
+
shared_strings = ["First String", "Second String"]
|
|
60
|
+
styles = {}
|
|
61
|
+
puts "\n--- 4. WorksheetParser Row Scanning ---"
|
|
62
|
+
Benchmark.ips do |x|
|
|
63
|
+
x.config(time: ips_time, warmup: ips_warmup)
|
|
64
|
+
x.report("WorksheetParser.fast_scan_row_values") do
|
|
65
|
+
Xlsxrb::Ooxml::WorksheetParser.fast_scan_row_values(sample_row_xml, 0, sample_row_xml.bytesize, shared_strings, styles)
|
|
66
|
+
end
|
|
67
|
+
x.compare!
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
puts "\nMicrobenchmarks complete."
|
data/lib/xlsxrb/chart_builder.rb
CHANGED
|
@@ -184,6 +184,18 @@ module Xlsxrb
|
|
|
184
184
|
@options[:value_axis] = kwargs.empty? ? args.first : kwargs
|
|
185
185
|
end
|
|
186
186
|
|
|
187
|
+
# Configures the secondary value axis properties for combo charts.
|
|
188
|
+
#
|
|
189
|
+
# @param args [Array] Positional arguments.
|
|
190
|
+
# @param kwargs [Hash] Keyword arguments.
|
|
191
|
+
# @return [Object]
|
|
192
|
+
# @api public
|
|
193
|
+
#: (*(Hash[Symbol, String | Numeric | bool | nil] | String) args, **String | Numeric | bool | nil kwargs) -> (Hash[Symbol, String | Numeric | bool | nil] | String)
|
|
194
|
+
def secondary_value_axis(*args, **kwargs)
|
|
195
|
+
@options[:secondary_value_axis] = kwargs.empty? ? args.first : kwargs
|
|
196
|
+
end
|
|
197
|
+
alias secondary_axis secondary_value_axis
|
|
198
|
+
|
|
187
199
|
# Configures whether to show the legend key in data labels.
|
|
188
200
|
#
|
|
189
201
|
# @param args [Array] Positional arguments.
|
|
@@ -322,12 +334,39 @@ module Xlsxrb
|
|
|
322
334
|
#
|
|
323
335
|
# @param args [Array] Positional arguments.
|
|
324
336
|
# @param kwargs [Hash] Keyword arguments.
|
|
325
|
-
# @return [String]
|
|
337
|
+
# @return [Symbol, String, Hash, nil]
|
|
326
338
|
# @api public
|
|
327
|
-
#: (*(String) args, **String | Numeric | bool | nil kwargs) -> String
|
|
339
|
+
#: (*(String | Symbol) args, **String | Numeric | bool | nil kwargs) -> (String | Symbol | Hash[Symbol, untyped] | nil)
|
|
328
340
|
def type(*args, **kwargs)
|
|
329
341
|
@options[:type] = kwargs.empty? ? args.first : kwargs
|
|
330
342
|
end
|
|
343
|
+
|
|
344
|
+
# Configures whether this series uses the secondary value axis.
|
|
345
|
+
#
|
|
346
|
+
# @param args [Array] Positional arguments.
|
|
347
|
+
# @param kwargs [Hash] Keyword arguments.
|
|
348
|
+
# @return [Boolean, Hash]
|
|
349
|
+
# @api public
|
|
350
|
+
#: (*bool args, **bool kwargs) -> (bool | Hash[Symbol, bool])
|
|
351
|
+
def secondary_axis(*args, **kwargs)
|
|
352
|
+
@options[:secondary_axis] = if kwargs.empty?
|
|
353
|
+
args.empty? || args.first
|
|
354
|
+
else
|
|
355
|
+
kwargs
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
# Configures the axis (:primary or :secondary) for this series.
|
|
360
|
+
#
|
|
361
|
+
# @param value [Symbol, String]
|
|
362
|
+
# @return [Symbol, String]
|
|
363
|
+
# @api public
|
|
364
|
+
#: (Symbol | String value) -> (Symbol | String)
|
|
365
|
+
def axis(value)
|
|
366
|
+
@options[:axis] = value
|
|
367
|
+
@options[:secondary_axis] = (value.to_s == "secondary")
|
|
368
|
+
value
|
|
369
|
+
end
|
|
331
370
|
end
|
|
332
371
|
end
|
|
333
372
|
end
|