Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). Thia is a

### Fixed

- Header/Footer images expand location. [Issue #484](https://github.com/PHPOffice/PhpSpreadsheet/issues/484) [Issue #1318](https://github.com/PHPOffice/PhpSpreadsheet/issues/1318) [PR #4572](https://github.com/PHPOffice/PhpSpreadsheet/pull/4572)
- Create uninitialized cell if used in calculation. [Issue #4558](https://github.com/PHPOffice/PhpSpreadsheet/issues/4558) [Issue #4530](https://github.com/PHPOffice/PhpSpreadsheet/issues/4530) [PR #4565](https://github.com/PHPOffice/PhpSpreadsheet/pull/4565)
- Shared/Date::isDateTime handle cells which calculate as arrays. [Issue #4557](https://github.com/PHPOffice/PhpSpreadsheet/issues/4557) [PR #4562](https://github.com/PHPOffice/PhpSpreadsheet/pull/4562)
- Xlsx Writer eliminate xml:space from non-text nodes. [Issue #4542](https://github.com/PHPOffice/PhpSpreadsheet/issues/4542) [PR #4556](https://github.com/PHPOffice/PhpSpreadsheet/pull/4556)## 2025-07-23 - 4.5.0
Expand Down
32 changes: 28 additions & 4 deletions docs/topics/recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,17 @@ $spreadsheet->getActiveSheet()->getHeaderFooter()
->setOddFooter('&L&B' . $spreadsheet->getProperties()->getTitle() . '&RPage &P of &N');
```

<a id='setDifferent'></a>
Notice the use of `oddHeader/Footer` above. This is, by default, the header used on all pages, not just the odd-numbered pages. You can specify a different header/footer for the first page and/or for even-numbered pages.
```php
$spreadsheet->getActiveSheet()->getHeaderFooter()
->setDifferentFirst(true);
// then as above except setFirstHeader/Footer rather than Odd
$spreadsheet->getActiveSheet()->getHeaderFooter()
->setDifferentOddEven(true);
// then as above except setEvenHeader/Footer rather than Odd
```

Substitution and formatting codes (starting with &) can be used inside
headers and footers. There is no required order in which these codes
must appear.
Expand Down Expand Up @@ -792,7 +803,7 @@ Code | Meaning
`&C` | Code for "center section". When two or more occurrences of this section marker exist, the contents from all markers are concatenated, in the order of appearance, and placed into the center section.
`&D` | Code for "date"
`&T` | Code for "time"
`&G` | Code for "picture as background" - Please make sure to add the image to the header/footer (see Tip for picture)
`&G` | Code for "picture as background" - Please make sure to add the image to the header/footer (see [Tip for picture](#Tip-for-picture))
`&U` | Code for "text single underline"
`&E` | Code for "double underline"
`&R` | Code for "right section". When two or more occurrences of this section marker exist, the contents from all markers are concatenated, in the order of appearance, and placed into the right section.
Expand Down Expand Up @@ -835,6 +846,7 @@ users may find it easier to rename test.xlsx to test.zip, unzip it, and
inspect directly the contents of the relevant xl/worksheets/sheetX.xml
to find the codes for header/footer.

<a id='Tip-for-picture'></a>
**Tip for picture**

```php
Expand All @@ -844,22 +856,34 @@ $drawing->setPath('./images/PhpSpreadsheet_logo.png');
$drawing->setHeight(36);
$spreadsheet->getActiveSheet()
->getHeaderFooter()
->addImage($drawing, \PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooter::IMAGE_HEADER_LEFT);
->addImage(
$drawing,
\PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooter::IMAGE_HEADER_LEFT
);
```
If you want your image to be used only on the first page or only on even pages, use, for example, `HeaderFooter::IMAGE_FOOTER_CENTER_EVEN`.
You must still call [`setDifferentFirst/Even`](#setDifferent) for this to work.
This will work only for Xlsx.

### Setting printing breaks on a row or column

To set a print break, use the following code, which sets a row break on
row 10.

```php
$spreadsheet->getActiveSheet()->setBreak('A10', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW);
$spreadsheet->getActiveSheet()->setBreak(
'A10',
\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW
);
```

The following line of code sets a print break on column D:

```php
$spreadsheet->getActiveSheet()->setBreak('D10', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_COLUMN);
$spreadsheet->getActiveSheet()->setBreak(
'D10',
\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_COLUMN
);
```

### Show/hide gridlines when printing
Expand Down
62 changes: 44 additions & 18 deletions src/PhpSpreadsheet/Worksheet/HeaderFooter.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,29 @@ class HeaderFooter
{
// Header/footer image location
const IMAGE_HEADER_LEFT = 'LH';
const IMAGE_HEADER_LEFT_ODD = 'LH';
const IMAGE_HEADER_LEFT_FIRST = 'LHFIRST';
const IMAGE_HEADER_LEFT_EVEN = 'LHEVEN';
const IMAGE_HEADER_CENTER = 'CH';
const IMAGE_HEADER_CENTER_ODD = 'CH';
const IMAGE_HEADER_CENTER_FIRST = 'CHFIRST';
const IMAGE_HEADER_CENTER_EVEN = 'CHEVEN';
const IMAGE_HEADER_RIGHT = 'RH';
const IMAGE_HEADER_RIGHT_ODD = 'RH';
const IMAGE_HEADER_RIGHT_FIRST = 'RHFIRST';
const IMAGE_HEADER_RIGHT_EVEN = 'RHEVEN';
const IMAGE_FOOTER_LEFT = 'LF';
const IMAGE_FOOTER_LEFT_ODD = 'LF';
const IMAGE_FOOTER_LEFT_FIRST = 'LFFIRST';
const IMAGE_FOOTER_LEFT_EVEN = 'LFEVEN';
const IMAGE_FOOTER_CENTER = 'CF';
const IMAGE_FOOTER_CENTER_ODD = 'CF';
const IMAGE_FOOTER_CENTER_FIRST = 'CFFIRST';
const IMAGE_FOOTER_CENTER_EVEN = 'CFEVEN';
const IMAGE_FOOTER_RIGHT = 'RF';
const IMAGE_FOOTER_RIGHT_ODD = 'RF';
const IMAGE_FOOTER_RIGHT_FIRST = 'RFFIRST';
const IMAGE_FOOTER_RIGHT_EVEN = 'RFEVEN';

/**
* OddHeader.
Expand Down Expand Up @@ -377,32 +395,40 @@ public function setImages(array $images): static
return $this;
}

private const IMAGE_SORT_ORDER = [
self::IMAGE_HEADER_LEFT,
self::IMAGE_HEADER_LEFT_FIRST,
self::IMAGE_HEADER_LEFT_EVEN,
self::IMAGE_HEADER_CENTER,
self::IMAGE_HEADER_CENTER_FIRST,
self::IMAGE_HEADER_CENTER_EVEN,
self::IMAGE_HEADER_RIGHT,
self::IMAGE_HEADER_RIGHT_FIRST,
self::IMAGE_HEADER_RIGHT_EVEN,
self::IMAGE_FOOTER_LEFT,
self::IMAGE_FOOTER_LEFT_FIRST,
self::IMAGE_FOOTER_LEFT_EVEN,
self::IMAGE_FOOTER_CENTER,
self::IMAGE_FOOTER_CENTER_FIRST,
self::IMAGE_FOOTER_CENTER_EVEN,
self::IMAGE_FOOTER_RIGHT,
self::IMAGE_FOOTER_RIGHT_FIRST,
self::IMAGE_FOOTER_RIGHT_EVEN,
];

/**
* Get header/footer images.
*
* @return HeaderFooterDrawing[]
*/
public function getImages(): array
{
// Sort array
// Sort array - not sure why needed
$images = [];
if (isset($this->headerFooterImages[self::IMAGE_HEADER_LEFT])) {
$images[self::IMAGE_HEADER_LEFT] = $this->headerFooterImages[self::IMAGE_HEADER_LEFT];
}
if (isset($this->headerFooterImages[self::IMAGE_HEADER_CENTER])) {
$images[self::IMAGE_HEADER_CENTER] = $this->headerFooterImages[self::IMAGE_HEADER_CENTER];
}
if (isset($this->headerFooterImages[self::IMAGE_HEADER_RIGHT])) {
$images[self::IMAGE_HEADER_RIGHT] = $this->headerFooterImages[self::IMAGE_HEADER_RIGHT];
}
if (isset($this->headerFooterImages[self::IMAGE_FOOTER_LEFT])) {
$images[self::IMAGE_FOOTER_LEFT] = $this->headerFooterImages[self::IMAGE_FOOTER_LEFT];
}
if (isset($this->headerFooterImages[self::IMAGE_FOOTER_CENTER])) {
$images[self::IMAGE_FOOTER_CENTER] = $this->headerFooterImages[self::IMAGE_FOOTER_CENTER];
}
if (isset($this->headerFooterImages[self::IMAGE_FOOTER_RIGHT])) {
$images[self::IMAGE_FOOTER_RIGHT] = $this->headerFooterImages[self::IMAGE_FOOTER_RIGHT];
foreach (self::IMAGE_SORT_ORDER as $key) {
if (isset($this->headerFooterImages[$key])) {
$images[$key] = $this->headerFooterImages[$key];
}
}
$this->headerFooterImages = $images;

Expand Down
56 changes: 56 additions & 0 deletions tests/PhpSpreadsheetTests/Writer/Xlsx/Issue484Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooter;
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\SheetView;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;

class Issue484Test extends AbstractFunctional
{
public function testHeaderFooter(): void
{
$spreadsheet = new Spreadsheet();
$headerImage = new HeaderFooterDrawing();
$headerImage->setName('Header Logo');
$headerImage->setPath('samples/images/blue_square.png');
$headerImage->setHeight(12);
$footerImage = new HeaderFooterDrawing();
$footerImage->setName('Footer Logo');
$footerImage->setPath('samples/images/paid.png');
$footerImage->setHeight(12);

$worksheet = $spreadsheet->getActiveSheet();
$worksheet->getSheetView()
->setView(SheetView::SHEETVIEW_PAGE_LAYOUT);

$worksheet->getHeaderFooter()->setDifferentFirst(true);
$worksheet->getHeaderFooter()->setFirstHeader('&C&G&R&D');
$worksheet->getHeaderFooter()->addImage($headerImage, HeaderFooter::IMAGE_HEADER_CENTER_FIRST);

$worksheet->getHeaderFooter()->setDifferentOddEven(true);
$worksheet->getHeaderFooter()->setEvenHeader('&L&G&R&D');
$worksheet->getHeaderFooter()->addImage($headerImage, HeaderFooter::IMAGE_HEADER_LEFT_EVEN);
$worksheet->getHeaderFooter()->setEvenFooter('&C&G&R&D');
$worksheet->getHeaderFooter()->addImage($footerImage, HeaderFooter::IMAGE_FOOTER_CENTER_EVEN);

$worksheet->getHeaderFooter()->setOddHeader('&C&D');

for ($currentRow = 1; $currentRow < 130; ++$currentRow) {
$worksheet->setCellValue("A$currentRow", 'Bill');
$worksheet->setCellValue("B$currentRow", 'Smith');
}

// Save spreadsheet to file and read it back
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
$spreadsheet->disconnectWorksheets();
$sheet = $reloadedSpreadsheet->getActiveSheet();
$images = $sheet->getHeaderFooter()->getImages();
self::assertSame(['LHEVEN', 'CHFIRST', 'CFEVEN'], array_keys($images));
$reloadedSpreadsheet->disconnectWorksheets();
}
}