Skip to content

Commit 52b3506

Browse files
committed
HTML Writer: Enable footnotes and endnotes
1 parent 1ed13cc commit 52b3506

File tree

3 files changed

+92
-18
lines changed

3 files changed

+92
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ This release marked heavy refactorings on internal code structure with the creat
3232
- ListItem: Ability to create custom list and reset list number - @ivanlanin GH-10 GH-198
3333
- ODT Writer: Basic table writing support - @ivanlanin
3434
- Image: Keep image aspect ratio if only 1 dimension styled - @japonicus GH-194
35-
- HTML Writer: Basic HTML writer: text, textrun, link, title, textbreak, table, image (as Base64) - @ivanlanin GH-203 GH-67 GH-147
35+
- HTML Writer: Basic HTML writer: text, textrun, link, title, textbreak, table, image (as Base64), footnote, endnote - @ivanlanin GH-203 GH-67 GH-147
3636
- PDF Writer: Basic PDF writer using DomPDF: All HTML element except image - @ivanlanin GH-68
3737

3838
### Bugfixes

docs/intro.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ Writers
101101
+ +-----------------------+--------+-------+-------+-------+-------+
102102
| | Footer || | | | |
103103
+ +-----------------------+--------+-------+-------+-------+-------+
104-
| | Footnote || | | | |
104+
| | Footnote || | | | |
105105
+ +-----------------------+--------+-------+-------+-------+-------+
106-
| | Endnote || | | | |
106+
| | Endnote || | | | |
107107
+-------------------------+-----------------------+--------+-------+-------+-------+-------+
108108
| **Graphs** | 2D basic graphs | | | | | |
109109
+ +-----------------------+--------+-------+-------+-------+-------+

src/PhpWord/Writer/HTML.php

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
use PhpOffice\PhpWord\Element\TextBreak;
2323
use PhpOffice\PhpWord\Element\TextRun;
2424
use PhpOffice\PhpWord\Element\Title;
25+
use PhpOffice\PhpWord\Endnotes;
2526
use PhpOffice\PhpWord\Exception\Exception;
27+
use PhpOffice\PhpWord\Footnotes;
2628
use PhpOffice\PhpWord\PhpWord;
2729
use PhpOffice\PhpWord\Style;
2830
use PhpOffice\PhpWord\Style\Font;
@@ -42,6 +44,13 @@ class HTML extends AbstractWriter implements WriterInterface
4244
*/
4345
protected $isPdf = false;
4446

47+
/**
48+
* Footnotes and endnotes collection
49+
*
50+
* @var array
51+
*/
52+
protected $notes = array();
53+
4554
/**
4655
* Create new instance
4756
*/
@@ -85,6 +94,7 @@ public function writeDocument()
8594
$html .= '</head>' . PHP_EOL;
8695
$html .= '<body>' . PHP_EOL;
8796
$html .= $this->writeHTMLBody();
97+
$html .= $this->writeNotes();
8898
$html .= '</body>' . PHP_EOL;
8999
$html .= '</html>' . PHP_EOL;
90100

@@ -181,6 +191,32 @@ private function writeHTMLBody()
181191
return $html;
182192
}
183193

194+
/**
195+
* Write footnote/endnote contents
196+
*/
197+
private function writeNotes()
198+
{
199+
$footnote = Footnotes::getElements();
200+
$endnote = Endnotes::getElements();
201+
$html = '';
202+
203+
if (count($this->notes) > 0) {
204+
$html .= "<hr />";
205+
foreach ($this->notes as $noteId => $noteMark) {
206+
$noteAnchor = "note-{$noteId}";
207+
list($noteType, $noteTypeId) = explode('-', $noteMark);
208+
$collection = $$noteType;
209+
if (array_key_exists($noteTypeId, $collection)) {
210+
$element = $collection[$noteTypeId];
211+
$content = "<a href=\"#{$noteMark}\" class=\"NoteRef\"><sup>{$noteId}</sup></a>" . $this->writeTextRun($element, true);
212+
$html .= "<p><a name=\"{$noteAnchor}\" />{$content}</p>" . PHP_EOL;
213+
}
214+
}
215+
}
216+
217+
return $html;
218+
}
219+
184220
/**
185221
* Get text
186222
*
@@ -226,19 +262,20 @@ private function writeText($text, $withoutP = false)
226262
}
227263

228264
/**
229-
* Get text run content
265+
* Write text run content
230266
*
231-
* @param TextRun $textrun
267+
* @param TextRun|Footnote|Endnote $textrun
232268
* @return string
233269
*/
234-
private function writeTextRun($textrun)
270+
private function writeTextRun($textrun, $withoutP = false)
235271
{
236272
$html = '';
237273
$elements = $textrun->getElements();
238274
if (count($elements) > 0) {
239275
$paragraphStyle = $textrun->getParagraphStyle();
240276
$spIsObject = ($paragraphStyle instanceof Paragraph);
241-
$html .= '<p';
277+
278+
$html .= $withoutP ? '<span' : '<p';
242279
if ($paragraphStyle) {
243280
if (!$spIsObject) {
244281
$html .= ' class="' . $paragraphStyle . '"';
@@ -262,7 +299,8 @@ private function writeTextRun($textrun)
262299
$html .= $this->writeFootnote($element);
263300
}
264301
}
265-
$html .= '</p>' . PHP_EOL;
302+
$html .= $withoutP ? '</span>' : '</p>';
303+
$html .= PHP_EOL;
266304
}
267305

268306
return $html;
@@ -279,11 +317,14 @@ private function writeLink($element, $withoutP = false)
279317
{
280318
$url = $element->getLinkSrc();
281319
$text = $element->getLinkName();
320+
if ($text == '') {
321+
$text = $url;
322+
}
282323
$html = '';
283324
if (!$withoutP) {
284325
$html .= "<p>" . PHP_EOL;
285326
}
286-
$html .= "<a href=\"{$url}'\">{$text}</a>" . PHP_EOL;
327+
$html .= "<a href=\"{$url}\">{$text}</a>" . PHP_EOL;
287328
if (!$withoutP) {
288329
$html .= "</p>" . PHP_EOL;
289330
}
@@ -467,7 +508,7 @@ private function writeObject($element, $withoutP = false)
467508
*/
468509
private function writeFootnote($element)
469510
{
470-
return $this->writeUnsupportedElement($element, true);
511+
return $this->writeNote($element);
471512
}
472513

473514
/**
@@ -478,7 +519,25 @@ private function writeFootnote($element)
478519
*/
479520
private function writeEndnote($element)
480521
{
481-
return $this->writeUnsupportedElement($element, true);
522+
return $this->writeNote($element);
523+
}
524+
525+
/**
526+
* Write footnote/endnote marks
527+
*
528+
* @param Footnote|Endnote $element
529+
* @return string
530+
*/
531+
private function writeNote($element)
532+
{
533+
$index = count($this->notes) + 1;
534+
$prefix = ($element instanceof Endnote) ? 'endnote' : 'footnote';
535+
$noteMark = $prefix . '-' . $element->getRelationId();
536+
$noteAnchor = "note-{$index}";
537+
$this->notes[$index] = $noteMark;
538+
$html = "<a name=\"{$noteMark}\"><a href=\"#{$noteAnchor}\" class=\"NoteRef\"><sup>{$index}</sup></a>";
539+
540+
return $html;
482541
}
483542

484543
/**
@@ -509,18 +568,33 @@ private function writeUnsupportedElement($element, $withoutP = false)
509568
*/
510569
private function writeStyles()
511570
{
512-
$bodyCss = array();
513571
$css = '<style>' . PHP_EOL;
514572

515573
// Default styles
516-
$bodyCss['font-family'] = "'" . $this->getPhpWord()->getDefaultFontName() . "'";
517-
$bodyCss['font-size'] = $this->getPhpWord()->getDefaultFontSize() . 'pt';
518-
$css .= '* ' . $this->assembleCss($bodyCss, true) . PHP_EOL;
574+
$defaultStyles = array(
575+
'*' => array(
576+
'font-family' => $this->getPhpWord()->getDefaultFontName(),
577+
'font-size' => $this->getPhpWord()->getDefaultFontSize() . 'pt',
578+
),
579+
'a.NoteRef' => array(
580+
'text-decoration' => 'none',
581+
),
582+
'hr' => array(
583+
'height' => '1px',
584+
'padding' => '0',
585+
'margin' => '1em 0',
586+
'border' => '0',
587+
'border-top' => '1px solid #CCC',
588+
),
589+
);
590+
foreach ($defaultStyles as $selector => $style) {
591+
$css .= $selector . ' ' . $this->assembleCss($style, true) . PHP_EOL;
592+
}
519593

520594
// Custom styles
521-
$styles = Style::getStyles();
522-
if (is_array($styles)) {
523-
foreach ($styles as $name => $style) {
595+
$customStyles = Style::getStyles();
596+
if (is_array($customStyles)) {
597+
foreach ($customStyles as $name => $style) {
524598
if ($style instanceof Font) {
525599
if ($style->getStyleType() == 'title') {
526600
$name = str_replace('Heading_', 'h', $name);

0 commit comments

Comments
 (0)