Skip to content

Commit 5b96bcc

Browse files
authored
Merge pull request #4313 from oleibman/issue4311b
Ods Reader Sheet Names with Period in Addresses and Formulas
2 parents eacaa25 + 9ca6846 commit 5b96bcc

File tree

4 files changed

+110
-5
lines changed

4 files changed

+110
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
2525

2626
### Fixed
2727

28-
- Nothing yet.
28+
- Ods Reader Sheet Names with Period. [Issue #4311](https://github.com/PHPOffice/PhpSpreadsheet/issues/4311) [PR #4313](https://github.com/PHPOffice/PhpSpreadsheet/pull/4313)
2929

3030
## 2025-01-11 - 3.8.0
3131

src/PhpSpreadsheet/Reader/Ods/FormulaTranslator.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,24 @@
66

77
class FormulaTranslator
88
{
9-
public static function convertToExcelAddressValue(string $openOfficeAddress): string
9+
private static function replaceQuotedPeriod(string $value): string
1010
{
11-
$excelAddress = $openOfficeAddress;
11+
$value2 = '';
12+
$quoted = false;
13+
foreach (mb_str_split($value, 1, 'UTF-8') as $char) {
14+
if ($char === "'") {
15+
$quoted = !$quoted;
16+
} elseif ($char === '.' && $quoted) {
17+
$char = "\u{fffe}";
18+
}
19+
$value2 .= $char;
20+
}
1221

22+
return $value2;
23+
}
24+
25+
public static function convertToExcelAddressValue(string $openOfficeAddress): string
26+
{
1327
// Cell range 3-d reference
1428
// As we don't support 3-d ranges, we're just going to take a quick and dirty approach
1529
// and assume that the second worksheet reference is the same as the first
@@ -20,15 +34,17 @@ public static function convertToExcelAddressValue(string $openOfficeAddress): st
2034
'/\$?([^\.]+)\.([^\.]+)/miu', // Cell reference in another sheet
2135
'/\.([^\.]+):\.([^\.]+)/miu', // Cell range reference
2236
'/\.([^\.]+)/miu', // Simple cell reference
37+
'/\\x{FFFE}/miu', // restore quoted periods
2338
],
2439
[
2540
'$1!$2:$4',
2641
'$1!$2:$3',
2742
'$1!$2',
2843
'$1:$2',
2944
'$1',
45+
'.',
3046
],
31-
$excelAddress
47+
self::replaceQuotedPeriod($openOfficeAddress)
3248
);
3349

3450
return $excelAddress;
@@ -52,14 +68,16 @@ public static function convertToExcelFormulaValue(string $openOfficeFormula): st
5268
'/\[\$?([^\.]+)\.([^\.]+)\]/miu', // Cell reference in another sheet
5369
'/\[\.([^\.]+):\.([^\.]+)\]/miu', // Cell range reference
5470
'/\[\.([^\.]+)\]/miu', // Simple cell reference
71+
'/\\x{FFFE}/miu', // restore quoted periods
5572
],
5673
[
5774
'$1!$2:$3',
5875
'$1!$2',
5976
'$1:$2',
6077
'$1',
78+
'.',
6179
],
62-
$value
80+
self::replaceQuotedPeriod($value)
6381
);
6482
// Convert references to defined names/formulae
6583
$value = str_replace('$$', '', $value);
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Reader\Ods;
6+
7+
use PhpOffice\PhpSpreadsheet\Reader\Ods\FormulaTranslator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class FormulaTranslatorTest extends TestCase
12+
{
13+
#[DataProvider('addressesProvider')]
14+
public function testAddresses(string $result, string $address): void
15+
{
16+
self::assertSame($result, FormulaTranslator::convertToExcelAddressValue($address));
17+
}
18+
19+
public static function addressesProvider(): array
20+
{
21+
return [
22+
'range period in sheet name' => ["'sheet1.bug'!a1:a5", "'sheet1.bug'.a1:'sheet1.bug'.a5"],
23+
'range special chars and period in sheet name' => ["'#sheet1.x'!a1:a5", "'#sheet1.x'.a1:'#sheet1.x'.a5"],
24+
'cell period in sheet name' => ["'sheet1.bug'!b9", "'sheet1.bug'.b9"],
25+
'range unquoted sheet name' => ['sheet1!b9:c12', 'sheet1.b9:sheet1.c12'],
26+
'range unquoted sheet name with $' => ['sheet1!$b9:c$12', 'sheet1.$b9:sheet1.c$12'],
27+
'range quoted sheet name with $' => ["'sheet1'!\$b9:c\$12", '\'sheet1\'.$b9:\'sheet1\'.c$12'],
28+
'cell unquoted sheet name' => ['sheet1!B$9', 'sheet1.B$9'],
29+
'range no sheet name all dollars' => ['$B$9:$C$12', '$B$9:$C$12'],
30+
'range no sheet name some dollars' => ['B$9:$C12', 'B$9:$C12'],
31+
'range no sheet name no dollars' => ['B9:C12', 'B9:C12'],
32+
];
33+
}
34+
35+
#[DataProvider('formulaProvider')]
36+
public function testFormulas(string $result, string $formula): void
37+
{
38+
self::assertSame($result, FormulaTranslator::convertToExcelFormulaValue($formula));
39+
}
40+
41+
public static function formulaProvider(): array
42+
{
43+
return [
44+
'ranges no sheet name' => [
45+
'SUM(A5:A7,B$5:$B7)',
46+
'SUM([.A5:.A7];[.B$5:.$B7])',
47+
],
48+
'ranges sheet name with period' => [
49+
'SUM(\'test.bug\'!A5:A7,\'test.bug\'!B5:B7)',
50+
'SUM([\'test.bug\'.A5:.A7];[\'test.bug\'.B5:.B7])',
51+
],
52+
'ranges unquoted sheet name' => [
53+
'SUM(testbug!A5:A7,testbug!B5:B7)',
54+
'SUM([testbug.A5:.A7];[testbug.B5:.B7])',
55+
],
56+
'ranges quoted sheet name without period' => [
57+
'SUM(\'testbug\'!A5:A7,\'testbug\'!B5:B7)',
58+
'SUM([\'testbug\'.A5:.A7];[\'testbug\'.B5:.B7])',
59+
],
60+
];
61+
}
62+
}

tests/PhpSpreadsheetTests/Writer/Ods/AutoFilterTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,29 @@ public function testAutoFilterWriter(): void
3232

3333
self::assertSame('A1:C9', $reloaded->getActiveSheet()->getAutoFilter()->getRange());
3434
}
35+
36+
public function testPeriodInSheetNames(): void
37+
{
38+
$spreadsheet = new Spreadsheet();
39+
$worksheet = $spreadsheet->getActiveSheet();
40+
$worksheet->setTitle('work.sheet');
41+
42+
$dataSet = [
43+
['Year', 'Quarter', 'Sales'],
44+
[2020, 'Q1', 100],
45+
[2020, 'Q2', 120],
46+
[2020, 'Q3', 140],
47+
[2020, 'Q4', 160],
48+
[2021, 'Q1', 180],
49+
[2021, 'Q2', 75],
50+
[2021, 'Q3', 0],
51+
[2021, 'Q4', 0],
52+
];
53+
$worksheet->fromArray($dataSet, null, 'A1');
54+
$worksheet->getAutoFilter()->setRange('A1:C9');
55+
56+
$reloaded = $this->writeAndReload($spreadsheet, 'Ods');
57+
58+
self::assertSame('A1:C9', $reloaded->getActiveSheet()->getAutoFilter()->getRange());
59+
}
3560
}

0 commit comments

Comments
 (0)