Skip to content

Commit 8d8bc23

Browse files
authored
Merge pull request #4572 from oleibman/issue484
Header/Footer Images Expand "Location"
2 parents 216db02 + a4ee68b commit 8d8bc23

File tree

4 files changed

+129
-22
lines changed

4 files changed

+129
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). Thia is a
3636

3737
### Fixed
3838

39+
- 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)
3940
- 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)
4041
- 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)
4142
- 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

docs/topics/recipes.md

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,17 @@ $spreadsheet->getActiveSheet()->getHeaderFooter()
763763
->setOddFooter('&L&B' . $spreadsheet->getProperties()->getTitle() . '&RPage &P of &N');
764764
```
765765

766+
<a id='setDifferent'></a>
767+
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.
768+
```php
769+
$spreadsheet->getActiveSheet()->getHeaderFooter()
770+
->setDifferentFirst(true);
771+
// then as above except setFirstHeader/Footer rather than Odd
772+
$spreadsheet->getActiveSheet()->getHeaderFooter()
773+
->setDifferentOddEven(true);
774+
// then as above except setEvenHeader/Footer rather than Odd
775+
```
776+
766777
Substitution and formatting codes (starting with &) can be used inside
767778
headers and footers. There is no required order in which these codes
768779
must appear.
@@ -792,7 +803,7 @@ Code | Meaning
792803
`&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.
793804
`&D` | Code for "date"
794805
`&T` | Code for "time"
795-
`&G` | Code for "picture as background" - Please make sure to add the image to the header/footer (see Tip for picture)
806+
`&G` | Code for "picture as background" - Please make sure to add the image to the header/footer (see [Tip for picture](#Tip-for-picture))
796807
`&U` | Code for "text single underline"
797808
`&E` | Code for "double underline"
798809
`&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.
@@ -835,6 +846,7 @@ users may find it easier to rename test.xlsx to test.zip, unzip it, and
835846
inspect directly the contents of the relevant xl/worksheets/sheetX.xml
836847
to find the codes for header/footer.
837848

849+
<a id='Tip-for-picture'></a>
838850
**Tip for picture**
839851

840852
```php
@@ -844,22 +856,34 @@ $drawing->setPath('./images/PhpSpreadsheet_logo.png');
844856
$drawing->setHeight(36);
845857
$spreadsheet->getActiveSheet()
846858
->getHeaderFooter()
847-
->addImage($drawing, \PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooter::IMAGE_HEADER_LEFT);
859+
->addImage(
860+
$drawing,
861+
\PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooter::IMAGE_HEADER_LEFT
862+
);
848863
```
864+
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`.
865+
You must still call [`setDifferentFirst/Even`](#setDifferent) for this to work.
866+
This will work only for Xlsx.
849867

850868
### Setting printing breaks on a row or column
851869

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

855873
```php
856-
$spreadsheet->getActiveSheet()->setBreak('A10', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW);
874+
$spreadsheet->getActiveSheet()->setBreak(
875+
'A10',
876+
\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW
877+
);
857878
```
858879

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

861882
```php
862-
$spreadsheet->getActiveSheet()->setBreak('D10', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_COLUMN);
883+
$spreadsheet->getActiveSheet()->setBreak(
884+
'D10',
885+
\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_COLUMN
886+
);
863887
```
864888

865889
### Show/hide gridlines when printing

src/PhpSpreadsheet/Worksheet/HeaderFooter.php

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,29 @@ class HeaderFooter
6767
{
6868
// Header/footer image location
6969
const IMAGE_HEADER_LEFT = 'LH';
70+
const IMAGE_HEADER_LEFT_ODD = 'LH';
71+
const IMAGE_HEADER_LEFT_FIRST = 'LHFIRST';
72+
const IMAGE_HEADER_LEFT_EVEN = 'LHEVEN';
7073
const IMAGE_HEADER_CENTER = 'CH';
74+
const IMAGE_HEADER_CENTER_ODD = 'CH';
75+
const IMAGE_HEADER_CENTER_FIRST = 'CHFIRST';
76+
const IMAGE_HEADER_CENTER_EVEN = 'CHEVEN';
7177
const IMAGE_HEADER_RIGHT = 'RH';
78+
const IMAGE_HEADER_RIGHT_ODD = 'RH';
79+
const IMAGE_HEADER_RIGHT_FIRST = 'RHFIRST';
80+
const IMAGE_HEADER_RIGHT_EVEN = 'RHEVEN';
7281
const IMAGE_FOOTER_LEFT = 'LF';
82+
const IMAGE_FOOTER_LEFT_ODD = 'LF';
83+
const IMAGE_FOOTER_LEFT_FIRST = 'LFFIRST';
84+
const IMAGE_FOOTER_LEFT_EVEN = 'LFEVEN';
7385
const IMAGE_FOOTER_CENTER = 'CF';
86+
const IMAGE_FOOTER_CENTER_ODD = 'CF';
87+
const IMAGE_FOOTER_CENTER_FIRST = 'CFFIRST';
88+
const IMAGE_FOOTER_CENTER_EVEN = 'CFEVEN';
7489
const IMAGE_FOOTER_RIGHT = 'RF';
90+
const IMAGE_FOOTER_RIGHT_ODD = 'RF';
91+
const IMAGE_FOOTER_RIGHT_FIRST = 'RFFIRST';
92+
const IMAGE_FOOTER_RIGHT_EVEN = 'RFEVEN';
7593

7694
/**
7795
* OddHeader.
@@ -377,32 +395,40 @@ public function setImages(array $images): static
377395
return $this;
378396
}
379397

398+
private const IMAGE_SORT_ORDER = [
399+
self::IMAGE_HEADER_LEFT,
400+
self::IMAGE_HEADER_LEFT_FIRST,
401+
self::IMAGE_HEADER_LEFT_EVEN,
402+
self::IMAGE_HEADER_CENTER,
403+
self::IMAGE_HEADER_CENTER_FIRST,
404+
self::IMAGE_HEADER_CENTER_EVEN,
405+
self::IMAGE_HEADER_RIGHT,
406+
self::IMAGE_HEADER_RIGHT_FIRST,
407+
self::IMAGE_HEADER_RIGHT_EVEN,
408+
self::IMAGE_FOOTER_LEFT,
409+
self::IMAGE_FOOTER_LEFT_FIRST,
410+
self::IMAGE_FOOTER_LEFT_EVEN,
411+
self::IMAGE_FOOTER_CENTER,
412+
self::IMAGE_FOOTER_CENTER_FIRST,
413+
self::IMAGE_FOOTER_CENTER_EVEN,
414+
self::IMAGE_FOOTER_RIGHT,
415+
self::IMAGE_FOOTER_RIGHT_FIRST,
416+
self::IMAGE_FOOTER_RIGHT_EVEN,
417+
];
418+
380419
/**
381420
* Get header/footer images.
382421
*
383422
* @return HeaderFooterDrawing[]
384423
*/
385424
public function getImages(): array
386425
{
387-
// Sort array
426+
// Sort array - not sure why needed
388427
$images = [];
389-
if (isset($this->headerFooterImages[self::IMAGE_HEADER_LEFT])) {
390-
$images[self::IMAGE_HEADER_LEFT] = $this->headerFooterImages[self::IMAGE_HEADER_LEFT];
391-
}
392-
if (isset($this->headerFooterImages[self::IMAGE_HEADER_CENTER])) {
393-
$images[self::IMAGE_HEADER_CENTER] = $this->headerFooterImages[self::IMAGE_HEADER_CENTER];
394-
}
395-
if (isset($this->headerFooterImages[self::IMAGE_HEADER_RIGHT])) {
396-
$images[self::IMAGE_HEADER_RIGHT] = $this->headerFooterImages[self::IMAGE_HEADER_RIGHT];
397-
}
398-
if (isset($this->headerFooterImages[self::IMAGE_FOOTER_LEFT])) {
399-
$images[self::IMAGE_FOOTER_LEFT] = $this->headerFooterImages[self::IMAGE_FOOTER_LEFT];
400-
}
401-
if (isset($this->headerFooterImages[self::IMAGE_FOOTER_CENTER])) {
402-
$images[self::IMAGE_FOOTER_CENTER] = $this->headerFooterImages[self::IMAGE_FOOTER_CENTER];
403-
}
404-
if (isset($this->headerFooterImages[self::IMAGE_FOOTER_RIGHT])) {
405-
$images[self::IMAGE_FOOTER_RIGHT] = $this->headerFooterImages[self::IMAGE_FOOTER_RIGHT];
428+
foreach (self::IMAGE_SORT_ORDER as $key) {
429+
if (isset($this->headerFooterImages[$key])) {
430+
$images[$key] = $this->headerFooterImages[$key];
431+
}
406432
}
407433
$this->headerFooterImages = $images;
408434

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
6+
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooter;
9+
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
10+
use PhpOffice\PhpSpreadsheet\Worksheet\SheetView;
11+
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
12+
13+
class Issue484Test extends AbstractFunctional
14+
{
15+
public function testHeaderFooter(): void
16+
{
17+
$spreadsheet = new Spreadsheet();
18+
$headerImage = new HeaderFooterDrawing();
19+
$headerImage->setName('Header Logo');
20+
$headerImage->setPath('samples/images/blue_square.png');
21+
$headerImage->setHeight(12);
22+
$footerImage = new HeaderFooterDrawing();
23+
$footerImage->setName('Footer Logo');
24+
$footerImage->setPath('samples/images/paid.png');
25+
$footerImage->setHeight(12);
26+
27+
$worksheet = $spreadsheet->getActiveSheet();
28+
$worksheet->getSheetView()
29+
->setView(SheetView::SHEETVIEW_PAGE_LAYOUT);
30+
31+
$worksheet->getHeaderFooter()->setDifferentFirst(true);
32+
$worksheet->getHeaderFooter()->setFirstHeader('&C&G&R&D');
33+
$worksheet->getHeaderFooter()->addImage($headerImage, HeaderFooter::IMAGE_HEADER_CENTER_FIRST);
34+
35+
$worksheet->getHeaderFooter()->setDifferentOddEven(true);
36+
$worksheet->getHeaderFooter()->setEvenHeader('&L&G&R&D');
37+
$worksheet->getHeaderFooter()->addImage($headerImage, HeaderFooter::IMAGE_HEADER_LEFT_EVEN);
38+
$worksheet->getHeaderFooter()->setEvenFooter('&C&G&R&D');
39+
$worksheet->getHeaderFooter()->addImage($footerImage, HeaderFooter::IMAGE_FOOTER_CENTER_EVEN);
40+
41+
$worksheet->getHeaderFooter()->setOddHeader('&C&D');
42+
43+
for ($currentRow = 1; $currentRow < 130; ++$currentRow) {
44+
$worksheet->setCellValue("A$currentRow", 'Bill');
45+
$worksheet->setCellValue("B$currentRow", 'Smith');
46+
}
47+
48+
// Save spreadsheet to file and read it back
49+
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
50+
$spreadsheet->disconnectWorksheets();
51+
$sheet = $reloadedSpreadsheet->getActiveSheet();
52+
$images = $sheet->getHeaderFooter()->getImages();
53+
self::assertSame(['LHEVEN', 'CHFIRST', 'CFEVEN'], array_keys($images));
54+
$reloadedSpreadsheet->disconnectWorksheets();
55+
}
56+
}

0 commit comments

Comments
 (0)