diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a08b56e15..64884728e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Added - Support for configuring a Chart Title's overlay [PR #3325](https://github.com/PHPOffice/PhpSpreadsheet/pull/3325) +- Support for fixed value divisor in fractional Number Format Masks [PR #3339](https://github.com/PHPOffice/PhpSpreadsheet/pull/3339) ### Changed diff --git a/src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php b/src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php index d1fc89fd00..fdcf98329b 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php +++ b/src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php @@ -26,23 +26,28 @@ public static function format($value, string $format): string $decimalLength = strlen($decimalPart); $decimalDivisor = 10 ** $decimalLength; - /** @var float */ - $GCD = MathTrig\Gcd::evaluate($decimalPart, $decimalDivisor); - /** @var float */ - $decimalPartx = $decimalPart; + preg_match('/(#?.*\?)\/(\?+|\d+)/', $format, $matches); + $formatIntegerPart = $matches[1]; - $adjustedDecimalPart = $decimalPartx / $GCD; - $adjustedDecimalDivisor = $decimalDivisor / $GCD; + if (is_numeric($matches[2])) { + $fractionDivisor = 100 / (int) $matches[2]; + } else { + /** @var float */ + $fractionDivisor = MathTrig\Gcd::evaluate((int) $decimalPart, $decimalDivisor); + } + + $adjustedDecimalPart = (int) round((int) $decimalPart / $fractionDivisor, 0); + $adjustedDecimalDivisor = $decimalDivisor / $fractionDivisor; - if ((strpos($format, '0') !== false)) { + if ((strpos($formatIntegerPart, '0') !== false)) { return "{$sign}{$integerPart} {$adjustedDecimalPart}/{$adjustedDecimalDivisor}"; - } elseif ((strpos($format, '#') !== false)) { + } elseif ((strpos($formatIntegerPart, '#') !== false)) { if ($integerPart == 0) { return "{$sign}{$adjustedDecimalPart}/{$adjustedDecimalDivisor}"; } return "{$sign}{$integerPart} {$adjustedDecimalPart}/{$adjustedDecimalDivisor}"; - } elseif ((substr($format, 0, 3) == '? ?')) { + } elseif ((substr($formatIntegerPart, 0, 3) == '? ?')) { if ($integerPart == 0) { $integerPart = ''; } diff --git a/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php b/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php index 989f33abc8..f089ebacfe 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php +++ b/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php @@ -226,7 +226,7 @@ public static function format($value, string $format): string $format = self::pregReplace('/0,+/', '0', $format); $format = self::pregReplace('/#,+/', '#', $format); } - if (preg_match('/#?.*\?\/\?/', $format, $m)) { + if (preg_match('/#?.*\?\/(\?+|\d+)/', $format)) { $value = FractionFormatter::format($value, $format); } else { // Handle the number itself diff --git a/tests/PhpSpreadsheetTests/Style/NumberFormatTest.php b/tests/PhpSpreadsheetTests/Style/NumberFormatTest.php index ff5db4cb45..d9f85d0b16 100644 --- a/tests/PhpSpreadsheetTests/Style/NumberFormatTest.php +++ b/tests/PhpSpreadsheetTests/Style/NumberFormatTest.php @@ -56,6 +56,23 @@ public function providerNumberFormat(): array return require 'tests/data/Style/NumberFormat.php'; } + /** + * @dataProvider providerNumberFormatFractions + * + * @param mixed $expectedResult + * @param mixed $args + */ + public function testFormatValueWithMaskFraction($expectedResult, ...$args): void + { + $result = NumberFormat::toFormattedString(...$args); + self::assertEquals($expectedResult, $result); + } + + public function providerNumberFormatFractions(): array + { + return require 'tests/data/Style/NumberFormatFractions.php'; + } + /** * @dataProvider providerNumberFormatDates * diff --git a/tests/data/Style/NumberFormat.php b/tests/data/Style/NumberFormat.php index 755a0c57bb..4bd6b48118 100644 --- a/tests/data/Style/NumberFormat.php +++ b/tests/data/Style/NumberFormat.php @@ -267,68 +267,6 @@ -0.123, '_(0.00%_;( 0.00% )', ], - // Fraction - [ - '5 1/4', - 5.25, - '# ???/???', - ], - // Vulgar Fraction - [ - '5 3/10', - 5.2999999999999998, - '# ???/???', - ], - [ - '21/4', - 5.25, - '???/???', - ], - [ - '0 3/4', - 0.75, - '0??/???', - ], - [ - '3/4', - 0.75, - '#??/???', - ], - [ - ' 3/4', - 0.75, - '? ??/???', - ], - [ - ' 3/4', - '0.75000', - '? ??/???', - ], - [ - '5 1/16', - 5.0625, - '? ??/???', - ], - [ - '- 5/8', - -0.625, - '? ??/???', - ], - [ - '0', - 0, - '? ??/???', - ], - [ - '0', - '0.000', - '? ??/???', - ], - [ - '-16', - '-016.0', - '? ??/???', - ], // Complex formats [ '(001) 2-3456-789', diff --git a/tests/data/Style/NumberFormatFractions.php b/tests/data/Style/NumberFormatFractions.php new file mode 100644 index 0000000000..0b592027b6 --- /dev/null +++ b/tests/data/Style/NumberFormatFractions.php @@ -0,0 +1,104 @@ +