Skip to content

Commit 22c4956

Browse files
authored
Merge branch 'master' into issue4269
2 parents c744f57 + 08e2260 commit 22c4956

File tree

16 files changed

+242
-84
lines changed

16 files changed

+242
-84
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ and this project adheres to [Semantic Versioning](https://semver.org).
2121

2222
### Deprecated
2323

24-
- Nothing yet.
24+
- Drawing::setIsUrl is unneeded. The property is set when setPath determines whether path is a url.
2525

2626
### Fixed
2727

2828
- Add forceFullCalc option to Xlsx Writer. [Issue #4269](https://github.com/PHPOffice/PhpSpreadsheet/issues/4269) [PR #4271](https://github.com/PHPOffice/PhpSpreadsheet/pull/4271)
29+
- More context options may be needed for http(s) image. [Php issue 17121](https://github.com/php/php-src/issues/17121) [PR #4276](https://github.com/PHPOffice/PhpSpreadsheet/pull/4276)
30+
- Several fixed to ODS Writer. [Issue #4261](https://github.com/PHPOffice/PhpSpreadsheet/issues/4261) [PR #4263](https://github.com/PHPOffice/PhpSpreadsheet/pull/4263) [PR #4264](https://github.com/PHPOffice/PhpSpreadsheet/pull/4264) [PR #4266](https://github.com/PHPOffice/PhpSpreadsheet/pull/4266)
2931

3032
## 2024-12-08 - 3.6.0
3133

src/PhpSpreadsheet/Collection/Memory/SimpleCache1.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
*
1010
* Alternative implementation should leverage off-memory, non-volatile storage
1111
* to reduce overall memory usage.
12+
*
13+
* Either SimpleCache1 or SimpleCache3, but not both, may be used.
14+
* For code coverage testing, it will always be SimpleCache3.
15+
*
16+
* @codeCoverageIgnore
1217
*/
1318
class SimpleCache1 implements CacheInterface
1419
{

src/PhpSpreadsheet/Worksheet/Drawing.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,21 @@ public function setPath(string $path, bool $verifyFile = true, ?ZipArchive $zip
111111
$this->isUrl = true;
112112
$ctx = null;
113113
// https://github.com/php/php-src/issues/16023
114-
if (str_starts_with($path, 'https:')) {
115-
$ctx = stream_context_create(['ssl' => ['crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT]]);
114+
// https://github.com/php/php-src/issues/17121
115+
if (str_starts_with($path, 'https:') || str_starts_with($path, 'http:')) {
116+
$ctxArray = [
117+
'http' => [
118+
'user_agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
119+
'header' => [
120+
//'Connection: keep-alive', // unacceptable performance
121+
'Accept: image/*;q=0.9,*/*;q=0.8',
122+
],
123+
],
124+
];
125+
if (str_starts_with($path, 'https:')) {
126+
$ctxArray['ssl'] = ['crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT];
127+
}
128+
$ctx = stream_context_create($ctxArray);
116129
}
117130
$imageContents = @file_get_contents($path, false, $ctx);
118131
if ($imageContents !== false) {
@@ -183,6 +196,8 @@ public function getIsURL(): bool
183196
* Set isURL.
184197
*
185198
* @return $this
199+
*
200+
* @deprecated 3.7.0 not needed, property is set by setPath
186201
*/
187202
public function setIsURL(bool $isUrl): self
188203
{

src/PhpSpreadsheet/Writer/Ods/Cell/Style.php

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class Style
2020
public const COLUMN_STYLE_PREFIX = 'co';
2121
public const ROW_STYLE_PREFIX = 'ro';
2222
public const TABLE_STYLE_PREFIX = 'ta';
23+
public const INDENT_TO_INCHES = 0.1043; // undocumented, used trial and error
2324

2425
private XMLWriter $writer;
2526

@@ -28,12 +29,13 @@ public function __construct(XMLWriter $writer)
2829
$this->writer = $writer;
2930
}
3031

31-
private function mapHorizontalAlignment(string $horizontalAlignment): string
32+
private function mapHorizontalAlignment(?string $horizontalAlignment): string
3233
{
3334
return match ($horizontalAlignment) {
3435
Alignment::HORIZONTAL_CENTER, Alignment::HORIZONTAL_CENTER_CONTINUOUS, Alignment::HORIZONTAL_DISTRIBUTED => 'center',
3536
Alignment::HORIZONTAL_RIGHT => 'end',
3637
Alignment::HORIZONTAL_FILL, Alignment::HORIZONTAL_JUSTIFY => 'justify',
38+
Alignment::HORIZONTAL_GENERAL, '', null => '',
3739
default => 'start',
3840
};
3941
}
@@ -145,8 +147,10 @@ private function writeCellProperties(CellStyle $style): void
145147
{
146148
// Align
147149
$hAlign = $style->getAlignment()->getHorizontal();
150+
$hAlign = $this->mapHorizontalAlignment($hAlign);
148151
$vAlign = $style->getAlignment()->getVertical();
149152
$wrap = $style->getAlignment()->getWrapText();
153+
$indent = $style->getAlignment()->getIndent();
150154

151155
$this->writer->startElement('style:table-cell-properties');
152156
if (!empty($vAlign) || $wrap) {
@@ -168,10 +172,16 @@ private function writeCellProperties(CellStyle $style): void
168172

169173
$this->writer->endElement();
170174

171-
if (!empty($hAlign)) {
172-
$hAlign = $this->mapHorizontalAlignment($hAlign);
173-
$this->writer->startElement('style:paragraph-properties');
174-
$this->writer->writeAttribute('fo:text-align', $hAlign);
175+
if ($hAlign !== '' || !empty($indent)) {
176+
$this->writer
177+
->startElement('style:paragraph-properties');
178+
if ($hAlign !== '') {
179+
$this->writer->writeAttribute('fo:text-align', $hAlign);
180+
}
181+
if (!empty($indent)) {
182+
$indentString = sprintf('%.4f', $indent * self::INDENT_TO_INCHES) . 'in';
183+
$this->writer->writeAttribute('fo:margin-left', $indentString);
184+
}
175185
$this->writer->endElement();
176186
}
177187
}
@@ -289,6 +299,7 @@ public function writeTableStyle(Worksheet $worksheet, int $sheetId): void
289299
'style:name',
290300
sprintf('%s%d', self::TABLE_STYLE_PREFIX, $sheetId)
291301
);
302+
$this->writer->writeAttribute('style:master-page-name', 'Default');
292303

293304
$this->writer->startElement('style:table-properties');
294305

src/PhpSpreadsheet/Writer/Ods/Content.php

Lines changed: 21 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@
2020
*/
2121
class Content extends WriterPart
2222
{
23-
const NUMBER_COLS_REPEATED_MAX = 1024;
24-
const NUMBER_ROWS_REPEATED_MAX = 1048576;
25-
2623
private Formula $formulaConvertor;
2724

2825
/**
@@ -142,7 +139,6 @@ private function writeSheets(XMLWriter $objWriter): void
142139
sprintf('%s_%d_%d', Style::COLUMN_STYLE_PREFIX, $sheetIndex, $columnDimension->getColumnNumeric())
143140
);
144141
$objWriter->writeAttribute('table:default-cell-style-name', 'ce0');
145-
// $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX);
146142
$objWriter->endElement();
147143
}
148144
$this->writeRows($objWriter, $spreadsheet->getSheet($sheetIndex), $sheetIndex);
@@ -155,34 +151,33 @@ private function writeSheets(XMLWriter $objWriter): void
155151
*/
156152
private function writeRows(XMLWriter $objWriter, Worksheet $sheet, int $sheetIndex): void
157153
{
158-
$numberRowsRepeated = self::NUMBER_ROWS_REPEATED_MAX;
159-
$span_row = 0;
154+
$spanRow = 0;
160155
$rows = $sheet->getRowIterator();
161156
foreach ($rows as $row) {
162-
$cellIterator = $row->getCellIterator();
163-
--$numberRowsRepeated;
164-
if ($cellIterator->valid()) {
165-
$objWriter->startElement('table:table-row');
166-
if ($span_row) {
167-
if ($span_row > 1) {
168-
$objWriter->writeAttribute('table:number-rows-repeated', (string) $span_row);
169-
}
170-
$objWriter->startElement('table:table-cell');
171-
$objWriter->writeAttribute('table:number-columns-repeated', (string) self::NUMBER_COLS_REPEATED_MAX);
157+
$cellIterator = $row->getCellIterator(iterateOnlyExistingCells: true);
158+
$cellIterator->rewind();
159+
$rowStyleExists = $sheet->rowDimensionExists($row->getRowIndex()) && $sheet->getRowDimension($row->getRowIndex())->getRowHeight() > 0;
160+
if ($cellIterator->valid() || $rowStyleExists) {
161+
if ($spanRow) {
162+
$objWriter->startElement('table:table-row');
163+
$objWriter->writeAttribute(
164+
'table:number-rows-repeated',
165+
(string) $spanRow
166+
);
172167
$objWriter->endElement();
173-
$span_row = 0;
174-
} else {
175-
if ($sheet->rowDimensionExists($row->getRowIndex()) && $sheet->getRowDimension($row->getRowIndex())->getRowHeight() > 0) {
176-
$objWriter->writeAttribute(
177-
'table:style-name',
178-
sprintf('%s_%d_%d', Style::ROW_STYLE_PREFIX, $sheetIndex, $row->getRowIndex())
179-
);
180-
}
181-
$this->writeCells($objWriter, $cellIterator);
168+
$spanRow = 0;
169+
}
170+
$objWriter->startElement('table:table-row');
171+
if ($rowStyleExists) {
172+
$objWriter->writeAttribute(
173+
'table:style-name',
174+
sprintf('%s_%d_%d', Style::ROW_STYLE_PREFIX, $sheetIndex, $row->getRowIndex())
175+
);
182176
}
177+
$this->writeCells($objWriter, $cellIterator);
183178
$objWriter->endElement();
184179
} else {
185-
++$span_row;
180+
++$spanRow;
186181
}
187182
}
188183
}
@@ -192,7 +187,6 @@ private function writeRows(XMLWriter $objWriter, Worksheet $sheet, int $sheetInd
192187
*/
193188
private function writeCells(XMLWriter $objWriter, RowCellIterator $cells): void
194189
{
195-
$numberColsRepeated = self::NUMBER_COLS_REPEATED_MAX;
196190
$prevColumn = -1;
197191
foreach ($cells as $cell) {
198192
/** @var Cell $cell */
@@ -293,17 +287,6 @@ private function writeCells(XMLWriter $objWriter, RowCellIterator $cells): void
293287
$objWriter->endElement();
294288
$prevColumn = $column;
295289
}
296-
297-
$numberColsRepeated = $numberColsRepeated - $prevColumn - 1;
298-
if ($numberColsRepeated > 0) {
299-
if ($numberColsRepeated > 1) {
300-
$objWriter->startElement('table:table-cell');
301-
$objWriter->writeAttribute('table:number-columns-repeated', (string) $numberColsRepeated);
302-
$objWriter->endElement();
303-
} else {
304-
$objWriter->writeElement('table:table-cell');
305-
}
306-
}
307290
}
308291

309292
/**

src/PhpSpreadsheet/Writer/Ods/Styles.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,17 @@ public function write(): string
5656

5757
$objWriter->writeElement('office:font-face-decls');
5858
$objWriter->writeElement('office:styles');
59-
$objWriter->writeElement('office:automatic-styles');
60-
$objWriter->writeElement('office:master-styles');
59+
$objWriter->startElement('office:automatic-styles');
60+
$objWriter->startElement('style:page-layout');
61+
$objWriter->writeAttribute('style:name', 'Mpm1');
62+
$objWriter->endElement(); // style:page-layout
63+
$objWriter->endElement(); // office:automatic-styles
64+
$objWriter->startElement('office:master-styles');
65+
$objWriter->startElement('style:master-page');
66+
$objWriter->writeAttribute('style:name', 'Default');
67+
$objWriter->writeAttribute('style:page-layout-name', 'Mpm1');
68+
$objWriter->endElement(); //style:master-page
69+
$objWriter->endElement(); //office:master-styles
6170
$objWriter->endElement();
6271

6372
return $objWriter->getData();

src/PhpSpreadsheet/Writer/ZipStream2.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
use ZipStream\Option\Archive;
66
use ZipStream\ZipStream;
77

8+
/**
9+
* Either ZipStream2 or ZipStream3, but not both, may be used.
10+
* For code coverage testing, it will always be ZipStream3.
11+
*
12+
* @codeCoverageIgnore
13+
*/
814
class ZipStream2
915
{
1016
/**

tests/PhpSpreadsheetTests/Calculation/Functions/Logical/XorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public static function providerXOR(): array
1818
}
1919

2020
#[\PHPUnit\Framework\Attributes\DataProvider('providerXORLiteral')]
21-
public function xtestXORLiteral(mixed $expectedResult, string $formula): void
21+
public function testXORLiteral(mixed $expectedResult, float|string $formula): void
2222
{
2323
$sheet = $this->getSheet();
2424
$sheet->getCell('A1')->setValue("=XOR($formula)");

tests/PhpSpreadsheetTests/Reader/Html/HtmlImage2Test.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
class HtmlImage2Test extends TestCase
1212
{
13-
public function xtestCanInsertImageGoodProtocol(): void
13+
public function testCanInsertImageGoodProtocol(): void
1414
{
1515
if (getenv('SKIP_URL_IMAGE_TEST') === '1') {
1616
self::markTestSkipped('Skipped due to setting of environment variable');
@@ -31,7 +31,7 @@ public function xtestCanInsertImageGoodProtocol(): void
3131
self::assertEquals('A1', $drawing->getCoordinates());
3232
}
3333

34-
public function xtestCantInsertImageNotFound(): void
34+
public function testCantInsertImageNotFound(): void
3535
{
3636
if (getenv('SKIP_URL_IMAGE_TEST') === '1') {
3737
self::markTestSkipped('Skipped due to setting of environment variable');
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Reader\Ods;
6+
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
9+
10+
class RepeatEmptyCellsAndRowsTest extends AbstractFunctional
11+
{
12+
public function testSaveAndLoadHyperlinks(): void
13+
{
14+
$spreadsheetOld = new Spreadsheet();
15+
$oldSheet = $spreadsheetOld->getActiveSheet();
16+
$oldSheet->setCellValue('C1', 'xx');
17+
$oldSheet->setCellValue('G1', 'aa');
18+
$oldSheet->setCellValue('BB1', 'bb');
19+
$oldSheet->setCellValue('A6', 'aaa');
20+
$oldSheet->setCellValue('B7', 'bbb');
21+
$oldSheet->getRowDimension(10)->setRowHeight(12);
22+
$oldSheet->setCellValue('A12', 'this is A12');
23+
$style = $oldSheet->getStyle('B14:D14');
24+
$style->getFont()->setBold(true);
25+
$oldSheet->getCell('E15')->setValue('X');
26+
$oldSheet->mergeCells('E15:G16');
27+
$oldSheet->getCell('J15')->setValue('j15');
28+
$oldSheet->getCell('J16')->setValue('j16');
29+
$oldSheet->getCell('A19')->setValue('lastrow');
30+
$spreadsheet = $this->writeAndReload($spreadsheetOld, 'Ods');
31+
$spreadsheetOld->disconnectWorksheets();
32+
33+
$sheet = $spreadsheet->getActiveSheet();
34+
self::assertSame('xx', $sheet->getCell('C1')->getValue());
35+
self::assertSame('aa', $sheet->getCell('G1')->getValue());
36+
self::assertSame('bb', $sheet->getCell('BB1')->getValue());
37+
self::assertSame('aaa', $sheet->getCell('A6')->getValue());
38+
self::assertSame('bbb', $sheet->getCell('B7')->getValue());
39+
self::assertSame('this is A12', $sheet->getCell('A12')->getValue());
40+
// Read styles, including row height, not yet implemented for ODS
41+
self::assertSame('j15', $sheet->getCell('J15')->getValue());
42+
self::assertSame('j16', $sheet->getCell('J16')->getValue());
43+
self::assertSame(['E15:G16' => 'E15:G16'], $sheet->getMergeCells());
44+
self::assertSame('lastrow', $sheet->getCell('A19')->getValue());
45+
46+
$spreadsheet->disconnectWorksheets();
47+
}
48+
}

0 commit comments

Comments
 (0)