Commit Graph

342 Commits

Author SHA1 Message Date
Nebojša Zlatanović adde74b4e6 Fix DIMENSIONS record to use 0-based indices for both rows AND columns
The initial fix only converted column indices to 0-based, but overlooked
that row indices also need the same treatment per BIFF8 specification.

Changes:
- Convert firstRowIndex from 1-based to 0-based (subtract 1)
- Convert lastRowIndex from 1-based to 0-based (subtract 1)
- Update row capping logic for 65536 limit
- Fix test assertions to expect rwMic=0 and rwMac=5 (not 1 and 6)
- Enhanced documentation to clarify all DIMENSIONS indices are 0-based

This now matches the behavior observed in Excel-generated XLS files,
where a file with 3 rows × 5 columns shows: rwMic=0, rwMac=3, colMic=0, colMac=5

All tests pass (53 tests, 244 assertions).
2025-10-20 09:07:39 +02:00
Nebojša Zlatanović f2ea8b6cb9 Fix PHPStan type safety issues in DimensionsRecordTest
Add proper type assertions to handle potential false returns:
- Assert file_get_contents() returns string, not false
- Assert strpos() returns int, not false
- Assert unpack() returns array, not false

This resolves all 7 PHPStan errors reported in CI while maintaining
test functionality (12 assertions, all passing).
2025-10-17 13:35:12 +02:00
Nebojša Zlatanović b9857e53d5 Improve DIMENSIONS record fix with better code coverage and unit tests
This commit refines the BIFF8 DIMENSIONS record fix by:

1. **Optimized column index capping**: Replaced the if-statement with min()
   function to ensure lastColumnIndex never exceeds 255, improving code
   coverage and eliminating unreachable branches in unit tests.

2. **Added comprehensive unit tests**: Created DimensionsRecordTest.php which
   directly parses the binary DIMENSIONS record (0x0200) from XLS files to
   verify correct 0-based column indices.

The tests validate:
- colMic (first column) = 0 for column A (was incorrectly 1 before fix)
- colMac (last column + 1) uses proper 0-based indexing
- Column indices are correctly capped at 255 (BIFF8 limit)

These tests fail without the fix and pass with it, ensuring the DIMENSIONS
record is correctly written for compatibility with legacy XLS parsers that
expect 0-based column indices per the BIFF8 specification.
2025-10-17 13:26:55 +02:00
oleibman b5ba4ffa24 Merge pull request #4655 from oleibman/issue850
Better Support For Style Alignment Read Order
2025-10-11 19:26:32 +00:00
oleibman b1c5c64060 Tweak 2025-09-30 07:33:04 -07:00
oleibman a1ffd3473b Offer Tcpdf Interface Which Throws Exception Rather than Die
By default, TCPDF will die sometimes rather than throwing exception. And this is controlled by a defined constant in the global namespace, not by an instance property. Ugh! Using this class instead of the class which it extends will probably be suitable for most users. But not for those who have customized their config file. Which is why this isn't the default, so that there is no breaking change for those users. Note that if both TCPDF and TcpdfNoDie are used in the same process, the first one used "wins" the battle of the defines.
2025-09-29 22:25:20 -07:00
oleibman 8c70086f0e Html Better Support for Indent 2025-09-20 15:26:49 -07:00
oleibman dbff58a532 Better Support For Style Alignment Read Order
Fix #850 (marked stale many years ago, but now reopened). User had a typo in their script which would have caused problems no matter what. However, it exposed another problem. Style Alignment Read Order was supported only by the Xlsx Reader and Writer, but it could have been supported pretty easily for most other formats. This PR adds support for the following:

- Xls (read and write)
- Html (write, and read using inline styles). Html reader does not yet process most classes.
- Pdf (write). PhpSpreadsheet does not have a Pdf reader.
- Ods (write). PhpSpreadsheet does not yet support reading most Ods styles.
- Xml (read). PhpSpreadsheet does not have an Xml writer.
- Gnumeric (no change). It appears that the Gnumeric product does not support this attribute.
- Csv (no change). Csv does not support any styles.
- Slk (no change). Slk does not support non-Latin characters, so this attribute doesn't make sense for it.
2025-09-19 14:15:05 -07:00
oleibman fff2373b39 Merge branch 'master' into issue4584 2025-09-16 23:48:50 -07:00
oleibman 9bb090d64e Found an Untested String Increment Operator 2025-09-16 13:00:09 -07:00
oleibman f3e2eba517 Additional Coverage 2025-09-15 19:48:43 -07:00
oleibman abf357ea90 1904 Calendar 2025-09-14 08:08:46 -07:00
oleibman 9df8ceb5c0 Minor Improvements to Reader/Writer Coverage 2025-09-14 00:18:08 -07:00
oleibman c07675a59c Merge pull request #4641 from oleibman/issue4638
Proper Output for BASE Function
2025-09-12 03:29:16 +00:00
oleibman 2d9375f847 Wrapped Cells and Default Row Height
Fix #4584. As discussed there, MS has implemented this situation in a way that I frankly do not understand. Marking a row with a wrapped cell to *not* use the default row height seems to affect every other populated row. Very odd. Nevertheless, adding a new `customFormat` boolean property to RowDimension seems to be a way to resolve this problem. Note that the Xml generated by Excel already outputs a `customFormat` attribute when a style is applied to a row. This PR just adds that property for when it is needed for rowHeight. It need not, and should not, be set by the application when a style is applied to the row; PhpSpreadsheet will take care of that on its own.

Although the problem was raised for Xlsx format (I think), I investigated other relevant output formats as well. Html tends to do its own cell wrapping. I think the results after this change match the results before.

Xls already handled the output side of this situation without difficulty. A change was needed on the input side, and is included as part of this PR.

Ods did not handle default row height at all. As with other style properties, it is difficult to handle this on the input side, and this PR does nothing to improve that situation. However, we are, at least, able to provide some relief on the output side. Not complete relief - in order to apply the default size to unpopulated rows, we'd need to at least populate them with the `table:number-rows-repeated` attribute, and that has often been a source of problems, so I'm not willing to go there yet. This PR just ensures that all populated rows will have the correct height.
2025-09-11 20:13:21 -07:00
oleibman f19a73fdd6 Proper Output for BASE Function
Fix #4638. Microsoft does not document BASE as being introduced in Excel 2010 or later, but apparently it was.
2025-09-10 07:34:50 -07:00
oleibman a88993dcc8 More Accurate Handling of Countries in validLocaleLanguages
This is a theoretical problem. Building FalseTrueArray only looks at languages, not countries. The only example we have is `pt` vs `pt_br`, and since True and False have the same translation for both locales, there is no problem. This PR will help it from becoming a problem in future if a new language/country combo is added.
2025-09-01 18:37:48 -07:00
oleibman e31c9246ed Merge pull request #4582 from oleibman/setaccessible
Remove Reflection::setAccessible From Tests
2025-08-18 06:43:32 +00:00
oleibman 029d79c3fc Remove Reflection::setAccessible From Tests
Method has no effect starting with Php 8.1.0, and will be deprecated in 8.5. Remove it from our test suite.
2025-08-17 23:21:17 -07:00
oleibman 653645c50b Merge pull request #4574 from issakujitsuk/icon-set
Add Conditional Formatting with IconSet
2025-08-17 03:01:08 +00:00
issa a0f9706855 Add Conditional Formatting tests for IconSet in Xlsx writer 2025-08-09 08:06:19 +09:00
oleibman 9c083e271d Header/Footer Images Expand "Location"
Fix #484. Fix #1318. Both went stale a long time ago. The images can currently specify their location as Left, Center, or Right. However, they also need to specify their type - first page, even pages, or odd pages (which is what is used when neither of the others is applicable). This is accomplished by adding 18 new constants to `Worksheet\HeaderFooter`, any of which can be specified as the second argument to `HeaderFooter::addImage`. Somewhat surprisingly, no changes are required to Xlsx Writer or Reader.

Xls Reader and Writer support header/footer, but do not support using images in them. No other readers nor writers support header/footer.
2025-08-04 18:15:19 -07:00
oleibman e78bca9668 Merge pull request #4556 from oleibman/xmlspace
Xlsx Writer Eliminate xml:space From Non-Text Nodes
2025-08-03 15:46:51 +00:00
oleibman 6d8fc67512 Merge branch 'master' into deprecations 2025-08-02 19:07:45 -07:00
oleibman 20ac2f1940 Merge pull request #4527 from oleibman/issue4522
Minor Breaking Change to DefaultValueBinder
2025-08-03 01:48:03 +00:00
oleibman 69b65d4e2a Xlsx Writer Eliminate xml:space From Non-Text Nodes
Fix #4542. PhpSpreadsheet has been writing attribute `xml:space="preserve"` to the `table` tag when writing a Table. According to the issue, Excel 2016 is treating the resulting file as corrupt. I do not have access to a version of Excel 2016 to confirm. This seems to be a bug with that release. Nevertheless, the OOXML spec, with over 100 references to `xml:space` does not indicate that it is a permitted attribute for `table`. It should only be specified for text nodes. This PR eliminates the undocumented, and unneeded, usage.

Investigating further, PhpSpreadsheet also writes this attribute for `workbook`, `styleSheet`, and `worksheet` tags. It is again undocumented and unneeded in those cases. Although all Excel releases, including 2016, apparently tolerate such usage, this PR also eliminates those.

Finally, there is one case where PhpSpreadsheet omits this tag when it is needed. When writing a cell whose data type is an inline string, and the string contains leading or trailing whitespace, the text tag needs to specify `xml:space`, and is now changed to do so.
2025-07-26 00:08:33 -07:00
oleibman a12a0d0271 Eliminate Deprecated Constants
In Theme, change COLOR_SCHEME_2013_PLUS_NAME to private (use COLOR_SCHEME_2013_2022_NAME instead), and delete COLOR_SCHEME_2013_PLUS (use COLOR_SCHEME_2013_2022).
2025-07-24 01:27:56 -07:00
oleibman b489fb3a17 Merge branch 'master' into issue4539 2025-07-21 21:23:15 -07:00
oleibman 3538c7e0af Merge branch 'master' into issue4537 2025-07-21 20:52:07 -07:00
oleibman 6b3cb42db2 Merge branch 'master' into issue4522 2025-07-19 16:54:27 -07:00
oleibman 2735c40179 StyleMerger Use Copy Of baseStyle
Using baseStyle directly can lead to the by-reference problems associated with Cell and Style.
2025-07-17 07:45:55 -07:00
oleibman ffeaa42583 Emulate Xlsx More Closely
Xlsx conditional styles cannot be used to change alignment, nor font family, nor font size.

Conditional::getStyle currently allocates a new style as an unconditional style; it is changed to do so a conditional ctyle.

Border should not be conditionally merged if borderStyle is OMIT.

Inline Css was generating `class="gridlines gridlinesp"` for all cells. It is changed to use the worksheet settings to decide which classes to use.
2025-07-16 22:11:53 -07:00
oleibman a15091737f Html Writer Conditional Formatting Inline Css
Fix #4539. Conditional Formatting was recently added to Html Writer. It works fine when not using inline Css. However, when using inline Css, the code inadvertently added 2 different `style` attributes (one for the unconditional style and one for the conditional style) to the same cell. This is not valid html, and results in losing the conditional styling. This PR combines the two `style` attributes into one, which will now come after the `class`, `colspan`, and `rowspan` attributes.

Aside from the new tests, this PR changes an unusually large number of existing tests. While this might normally be considered a red flag, it is not a problem here. All of the changes involve merely changing the order of attributes within html tags; none of them affect how the generated html would appear in a browser.
2025-07-15 22:22:34 -07:00
oleibman b82756de76 Don't Use htmlspecialchars When Formatting Xml
Fix #4537. PhpSpreadsheet currently changes apostrophes in text values to `'`. This is perfectly valid Xml. Issue was opened because R does not handle this correctly; this is unquestionably a bug on R's part. So I was not inclined to do anything about it. However ...

User suggested a change to how `htmlspecialchars` was called. Investigating the use of that routine in PhpSpreadsheet, I found that there was some double escaping going on for cells whose type was set to `TYPE_INLINE` - `htmlspecialchars` escaped the string correctly, but it was later written as Xml using a method which escaped the data a second time. So, a real bug in PhpSpreadsheet after all.

There was one call to `htmlspecialchars` in `Shared\XmlWriter`. I replaced `writeRaw(htmlspecialchars(...))` with `text(...)`. And one call in `Writer\Xlsx\Worksheet`, the source of the double escaping bug above; the call to `htmlspecialchars` can just be eliminated there.

Making those changes, the only remaining calls to `htmlspecialchars` are in `Writer\Html`, where they belong. As a bonus, apostrophes now wind up unescaped, so R will be satisfied (even though they should fix their bug).
2025-07-15 21:53:43 -07:00
oleibman 98f464b41f Writer Html/Pdf Support RTL Alignment of Table
Fix #1104, which went stale over 6 years ago, and is now reopened. This PR addresses the alignment of the table, not alignment of text. Support is added for the following:

- Html. Full support.
- Mpdf. Full support, except that, when mixed LTR and RTL worksheets are output, all tables will be on the left of the page (but will be properly aligned).
- Tcpdf. Full support when all worksheets being output are RTL; no support when mixed LTR and RTL worksheets are output.
- Dompdf. No support.
2025-07-14 00:22:54 -07:00
oleibman 170b058049 WIP Minor Breaking Change to DefaultValueBinder
Fix #4522. Although this is technically a breaking change, it is expected that very few, if any, existing programs will be affected. Nevertheless, DefaultValueBinder is implicitly used by the vast majority of programs out there, and I am reluctant to install a breaking change for something so widespread. So I will save this change for the next breaking release, which should be PhpSpreadsheet 5.0.0. There is no current schedule for that release.

For strings consisting entirely of digits, DefaultValueBinder treats the value as a string if greater than PHP_INT_MAX, or an int otherwise. This prevents the loss of precision in large integers. This treatment dates back to PHPExcel, and has been in place since 2014.

There are several problems with this approach. Excel itself maintains
[15 digits of precision](https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3).
So, string-vs-int should be decided at 999_999_999_999_999. This is much lower than PHP_INT_MAX for 64-bit, and much higher than for 32-bit. DefaultValueBinder is changed to use that new limit.

A second problem is that DefaultValueBinder is only making this adjustment for positive integers. It is changed to test absolute value.

A third problem is that DefaultValueBinder is making this adjustment only for strings, so that if you pass the number in as an int, it will not be adjusted (and will lose precision). It is changed to apply the same test for int.
2025-07-04 20:58:03 -07:00
oleibman 84acafc0bf Merge branch 'master' into writestyles 2025-06-17 21:35:23 -07:00
oleibman 0dbe154e29 Merge pull request #4503 from oleibman/issue4502
AutoColor for LibreOffice Dark Mode
2025-06-18 04:22:45 +00:00
oleibman 30369ca036 Merge pull request #4501 from oleibman/issue2760
Allow Xlsx Reader/Writer to Process Font Charset
2025-06-18 04:21:17 +00:00
oleibman d38117ffe6 Eliminate Dead Statement 2025-06-12 16:48:59 -07:00
oleibman 04cfc57457 Xlsx Style Writer Minor Refactoring
In our unit tests, we can test the Worksheet Xml which PhpSpreadsheet will generate without having to actually write the spreadsheet to and read it from disk. This refactoring allows us to do the same for Styles, as demonstrated in the new StylesWriterTest.
2025-06-12 15:51:48 -07:00
oleibman f0ca95f8f9 Tests and Documentation 2025-06-10 07:14:38 -07:00
oleibman b91a9ef3f4 AutoColor for LibreOffice Dark Mode
See discussion #4502. I am not sure of what needs to be involved here. This is especially so because Excel also offers AutoColor, but its dark mode looks significantly different than LibreOffice's.

So here's what I've come up with thus far.
- Add boolean autoColor with setter and getter to Font.
- If autoColor is true, ODS writer and XLSX Writer will record it in the xml, and will not write a font color. It is possible that both can co-exist; I'm just not sure how they are supposed to interact if that is the case.
- It makes most sense to me if the spreadsheet's default font specifies autoColor, but PhpSpreadsheet will not insist on that.
- Xlsx Reader will process autoColor. Like most other styles, Ods Reader is not yet set up to handle it.
- LibreOffice Calc should follow the autoColor declarations when it reads either an Xlsx or Ods spreadsheet created by PhpSpreadsheet.
2025-06-05 22:19:12 -07:00
oleibman eb486262fa Support Current Office Theme
A while back, Microsoft introduced changes to the default theme colors and fonts. We are adding support for this new theme so that users can use it easily on new spreadsheets if they wish. (It is already supported when using the Xlsx Reader to load an appropriate file.) The old theme was described by constants COLOR_SCHEME_2013_PLUS_NAME and COLOR_SCHEME_2013_PLUS; these are deprecated in favor of COLOR_SCHEME_2013_2022_NAME and COLOR_SCHEME_2013_2022. The new theme is described by constants COLOR_SCHEME_2023_PLUS_NAME and COLOR_SCHEME_2023_PLUS. PhpSpreadsheet's default theme remains COLOR_SCHEME_2007_2010, to avoid breaking changes.

A third optional parameter `$spreadsheet` is added to setThemeColorName. If specified, the default font names for the theme will be applied to the default style for the spreadsheet. You can thus use the new theme with its relatively new default `Aptos Narrow` font. IMHO, that isn't necessarily a good choice, but it is available. MS stores the new font in a different location than other system fonts, and that can lead to portability problems, e.g. if your spreadsheet uses Aptos and you export it to Html, browsers will not be able to find the font and a substitute font will be used.
2025-06-03 21:36:05 -07:00
oleibman 277c8d5615 Merge branch 'master' into issue1154 2025-06-01 09:17:22 -07:00
oleibman ba079b3967 Various Writers RichText TextElement Should Inherit Cell Style
Fix #1154, which went stale over 5 years ago, and is now reopened. RichText elements can be a Run, which sets its own style, or a TextElement, which doesn't. As the issue states, TextElement handling is inconsistent. This PR forces it to inherit the style of the cell. As implemented, this will change it to a Run when it is read in, but there should be no practical difference as far as the end-user is concerned. There is no change as far as Run is concerned. The user suggested 3 options - TextElement and Run both inherit for any unspecified style elements, TextElement inherits and Run does not, neither inherits. Option 2 makes most sense to me. Option 1 may not even be possible (we can't tell if, say, the user explicityl set Italic to false, or if that was just the default choice).

Tests are added for all of Xlsx, Xls, and Html. Html reading of RichText elements is not well-supported. The tests are fairly difficult to understand. A new sample is added to demonstrate this change.
2025-05-25 21:22:12 -07:00
oleibman ef81f19996 More Precision for Float to String Casts
Fix #3899. Supersedes PR #4476, which will be changed to draft status and closed if this PR is merged.

A standard cast from float to string in PHP can drop trailing decimal positions. This can lead to problems above and beyond the usual problems associated with floating point. See the superseded PR for a more complete explanation.

`StringHelper::convertToString` is changed for how it handles floats. It will now do separate casts for the whole and decimal parts, and then combine the results. This affects `Cell::getValueString` and `Cell::getCalculatedValueString`. Xlsx Writer will now invoke `convertToString` before writing  a float to Xml. Ods Writer already uses `getValueString`, so no change is needed there. Xls Writer writes its float values in binary, so no change is needed there. Tests are added for all 3 writers.

Aside from fixing some problems, it might appear that this change introduces some new problems. For instance, setting a cell to `12345.6789` will now result in `12345.67890000000079` in the Xml. This difference is an illusion, merely a consequence of floating point rounding. If you run the following check under PhpUnit, it will pass:
```php
self::assertSame(12345.6789, 12345.67890000000079);
```
2025-05-20 23:35:50 -07:00
oleibman 576ce504f9 Merge branch 'master' into issue56 2025-05-06 19:51:00 -07:00
oleibman 5c94de34a9 More Phpstan Level 10 Prep - Readers
Also upgrade Phpstan.
2025-05-03 21:18:55 -07:00
oleibman 92d3c1bc7d Xls Writer Treat Hyperlink Starting with # as Internal
Fix #56, which had gone stale but is now reopened. The problem which it reported with Xlsx Writer was fixed long ago. However, Xls Writer has 2 problems regarding hyperlinks. First, some logic which should have been `if ... elseif ... else ...` was coded as `if ... if ... unconditional ...`, resulting in multiple writes and a corrupt worksheet (which Excel does fix correctly). Second, it treated a hyperlink url starting with `#` (pointer to a cell in the same spreadsheet) as external, when it should be treated as internal.

Also changed `Hyperlink::isInternal` to recognize a starting `#`, and to use `str_starts_with('sheet://')` rather than `str_contains`.
2025-04-30 00:23:35 -07:00