From ff9b9f104b48a14aed07cc4ff29c40c078ea2ee0 Mon Sep 17 00:00:00 2001 From: "Christoph \"criztovyl\" Schulz" Date: Mon, 1 Jan 2018 12:39:59 +0100 Subject: [PATCH 01/12] Include comments in HTML writer Taken from LibreOffice. --- src/PhpSpreadsheet/Writer/Html.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index 7e1a0048a6..428b6053a1 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -830,6 +830,29 @@ public function buildCSS($generateSurroundingHTML = true) $css['html']['background-color'] = 'white'; } + // + // Comment CSS taken from LibreOffice (core) + // sc/source/filter/html/htmlexp.cxx cHTMLExport::WriteHeader() + // + + $css['a.comment-indicator:hover + comment'] = [ + "background" => "#ffd", + "position" => "absolute", + "display" => "block", + "border" => "1px solid black", + "padding" => "0.5em" + ]; + + $css['a.comment-indicator'] = [ + "background" => "red", + "display" => "inline-block", + "border" => "1px solid black", + "width" => "0.5em", + "height" => "0.5em" + ]; + + $css['comment']['display'] = 'none'; + // table { } $css['table']['border-collapse'] = 'collapse'; if (!$this->isPdf) { @@ -1385,6 +1408,13 @@ private function generateRow(Worksheet $pSheet, array $pValues, $pRow, $cellType } $html .= '>'; + // Taken from LibreOffice core + // https://github.com/LibreOffice/core/blob/9fc9bf3240f8c62ad7859947ab8a033ac1fe93fa/sc/source/filter/html/htmlexp.cxx#L1073-L1092 + + if(isset($pSheet->getComments()[$coordinate])) { + $html .= '' . nl2br($comment) . '' . PHP_EOL; + } + // Image? $html .= $this->writeImageInCell($pSheet, $coordinate); From 1b99b62c957e04cce9b262f5530cb86d3f27f677 Mon Sep 17 00:00:00 2001 From: "Christoph \"criztovyl\" Schulz" Date: Mon, 1 Jan 2018 13:48:51 +0100 Subject: [PATCH 02/12] Fix undefined variable --- src/PhpSpreadsheet/Writer/Html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index 428b6053a1..c35a4e4fdc 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -1412,7 +1412,7 @@ private function generateRow(Worksheet $pSheet, array $pValues, $pRow, $cellType // https://github.com/LibreOffice/core/blob/9fc9bf3240f8c62ad7859947ab8a033ac1fe93fa/sc/source/filter/html/htmlexp.cxx#L1073-L1092 if(isset($pSheet->getComments()[$coordinate])) { - $html .= '' . nl2br($comment) . '' . PHP_EOL; + $html .= '' . nl2br($pSheet->getComment($coordinate)) . '' . PHP_EOL; } // Image? From 58ea23e4621b5fc3960f50a37be4d73acf451a49 Mon Sep 17 00:00:00 2001 From: "Christoph \"criztovyl\" Schulz" Date: Mon, 1 Jan 2018 13:50:27 +0100 Subject: [PATCH 03/12] Changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdc240d9bb..c97e995d72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Include spreadsheet comments in HTML writer - [#308](https://github.com/PHPOffice/PhpSpreadsheet/issues/308) + ### Fixed - Better auto-detection of CSV separators - [#305](https://github.com/PHPOffice/PhpSpreadsheet/issues/305) From f648fdd695e3e51cbad93a0d7273faceccb4b4ec Mon Sep 17 00:00:00 2001 From: "Christoph \"criztovyl\" Schulz" Date: Mon, 1 Jan 2018 13:57:57 +0100 Subject: [PATCH 04/12] Code Style --- src/PhpSpreadsheet/Writer/Html.php | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index c35a4e4fdc..9f46587f15 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -836,19 +836,19 @@ public function buildCSS($generateSurroundingHTML = true) // $css['a.comment-indicator:hover + comment'] = [ - "background" => "#ffd", - "position" => "absolute", - "display" => "block", - "border" => "1px solid black", - "padding" => "0.5em" + 'background' => '#ffd', + 'position' => 'absolute', + 'display' => 'block', + 'border' => '1px solid black', + 'padding' => '0.5em' ]; $css['a.comment-indicator'] = [ - "background" => "red", - "display" => "inline-block", - "border" => "1px solid black", - "width" => "0.5em", - "height" => "0.5em" + 'background' => 'red', + 'display' => 'inline-block', + 'border' => '1px solid black', + 'width' => '0.5em', + 'height' => '0.5em' ]; $css['comment']['display'] = 'none'; @@ -1411,8 +1411,10 @@ private function generateRow(Worksheet $pSheet, array $pValues, $pRow, $cellType // Taken from LibreOffice core // https://github.com/LibreOffice/core/blob/9fc9bf3240f8c62ad7859947ab8a033ac1fe93fa/sc/source/filter/html/htmlexp.cxx#L1073-L1092 - if(isset($pSheet->getComments()[$coordinate])) { - $html .= '' . nl2br($pSheet->getComment($coordinate)) . '' . PHP_EOL; + if (isset($pSheet->getComments()[$coordinate])) { + $html .= ''; + $html .= '' . nl2br($pSheet->getComment($coordinate)) . ''; + $html .= PHP_EOL; } // Image? From 6e337780b3b5a3fbf792739bf1700db8b15b57d5 Mon Sep 17 00:00:00 2001 From: "Christoph \"criztovyl\" Schulz" Date: Mon, 1 Jan 2018 14:07:33 +0100 Subject: [PATCH 05/12] More Code Style --- src/PhpSpreadsheet/Writer/Html.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index 9f46587f15..04602359d6 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -840,7 +840,7 @@ public function buildCSS($generateSurroundingHTML = true) 'position' => 'absolute', 'display' => 'block', 'border' => '1px solid black', - 'padding' => '0.5em' + 'padding' => '0.5em', ]; $css['a.comment-indicator'] = [ @@ -848,7 +848,7 @@ public function buildCSS($generateSurroundingHTML = true) 'display' => 'inline-block', 'border' => '1px solid black', 'width' => '0.5em', - 'height' => '0.5em' + 'height' => '0.5em', ]; $css['comment']['display'] = 'none'; From 561fbc4e7f70cc05afc9abd137904d02021b6b57 Mon Sep 17 00:00:00 2001 From: "Christoph \"criztovyl\" Schulz" Date: Mon, 1 Jan 2018 21:20:21 +0100 Subject: [PATCH 06/12] Feature Reference --- docs/references/features-cross-reference.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/references/features-cross-reference.md b/docs/references/features-cross-reference.md index 4bac57a16c..2d28cd4dac 100644 --- a/docs/references/features-cross-reference.md +++ b/docs/references/features-cross-reference.md @@ -1231,14 +1231,14 @@ ● ● N/A - + ● 1 N/A Rich Text - ✖ 1 + ✖ 2 ✔ ✖ ✖ @@ -1256,7 +1256,7 @@ Alignment - ✖ 2 + ✖ 3 ✖ ✖ ✖ @@ -1568,5 +1568,6 @@ -1. Only BIFF8 files support Rich Text. Prior to that, comments could only be plain text -2. Only BIFF8 files support alignment and rotation. Prior to that, comments could only be unformatted text +1. Only text contents +2. Only BIFF8 files support Rich Text. Prior to that, comments could only be plain text +3. Only BIFF8 files support alignment and rotation. Prior to that, comments could only be unformatted text From 452980b589fc7655cd90534ccb429b7f8a64c87c Mon Sep 17 00:00:00 2001 From: "Christoph \"criztovyl\" Schulz" Date: Mon, 1 Jan 2018 23:13:48 +0100 Subject: [PATCH 07/12] Store comments in a div Preparing load comments from HTML reader - a custom COMMENT element would not work there. --- src/PhpSpreadsheet/Writer/Html.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index 04602359d6..148c226ad9 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -835,7 +835,7 @@ public function buildCSS($generateSurroundingHTML = true) // sc/source/filter/html/htmlexp.cxx cHTMLExport::WriteHeader() // - $css['a.comment-indicator:hover + comment'] = [ + $css['a.comment-indicator:hover + div.comment'] = [ 'background' => '#ffd', 'position' => 'absolute', 'display' => 'block', @@ -851,7 +851,7 @@ public function buildCSS($generateSurroundingHTML = true) 'height' => '0.5em', ]; - $css['comment']['display'] = 'none'; + $css['div.comment']['display'] = 'none'; // table { } $css['table']['border-collapse'] = 'collapse'; @@ -1413,7 +1413,7 @@ private function generateRow(Worksheet $pSheet, array $pValues, $pRow, $cellType if (isset($pSheet->getComments()[$coordinate])) { $html .= ''; - $html .= '' . nl2br($pSheet->getComment($coordinate)) . ''; + $html .= '
' . nl2br($pSheet->getComment($coordinate)->getText()->getPlainText()) . '
'; $html .= PHP_EOL; } From 05e32e652403e988b42d739bdda7a6181c7c3945 Mon Sep 17 00:00:00 2001 From: "Christoph \"criztovyl\" Schulz" Date: Mon, 1 Jan 2018 23:17:38 +0100 Subject: [PATCH 08/12] Import comments from HTML --- src/PhpSpreadsheet/Reader/Html.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/PhpSpreadsheet/Reader/Html.php b/src/PhpSpreadsheet/Reader/Html.php index a6720de68f..e2553202b1 100644 --- a/src/PhpSpreadsheet/Reader/Html.php +++ b/src/PhpSpreadsheet/Reader/Html.php @@ -312,6 +312,14 @@ protected function processDomElement(DOMNode $element, Worksheet $sheet, &$row, case 'em': case 'strong': case 'b': + if (isset($attributeArray['class']) && $attributeArray['class'] == 'comment') { + $sheet->getComment($column . $row) + ->getText() + ->createTextRun($child->textContent); + + break; + } + if ($cellContent > '') { $cellContent .= ' '; } @@ -354,6 +362,11 @@ protected function processDomElement(DOMNode $element, Worksheet $sheet, &$row, } break; + case 'class': + if ($attributeValue == 'comment-indicator') { + break; // Ignore - it's just a red square. + } + //else fall through } } $cellContent .= ' '; From 7f6db567c22be55d7cc83af986bc212f7e828a88 Mon Sep 17 00:00:00 2001 From: "Christoph \"criztovyl\" Schulz" Date: Mon, 1 Jan 2018 23:35:11 +0100 Subject: [PATCH 09/12] Add test for html comment reader --- .../Functional/HtmlCommentsTest.php | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php diff --git a/tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php b/tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php new file mode 100644 index 0000000000..cfd8f2efe8 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php @@ -0,0 +1,42 @@ +spreadsheet = new Spreadsheet(); + + $this->spreadsheet->getActiveSheet()->getCell('A1')->setValue('Comment'); + + return $this->spreadsheet->getActiveSheet() + ->getComment('A1') + ->getText()->createTextRun($this->value); + } + + private function doTheTest($string) + { + $reloadedSpreadsheet = $this->writeAndReload($this->spreadsheet, 'Html'); + + $actual = (string) $reloadedSpreadsheet->getActiveSheet()->getComment('A1')->getText()->getPlainText(); + self::assertSame($this->value, $actual, $string); + } + + public function testPlainTextComment() + { + $this->createComment(); + $this->doTheTest('should be able to read and write plain text comments from and to html as plain text'); + } + + public function testRichTextComment() + { + $this->createComment()->getFont()->setBold(true); + $this->doTheTest('should be able to read and write rich text comments from and to html as plain text'); + } +} From 4d79028fa26845995859695f43aa2708f92f65fe Mon Sep 17 00:00:00 2001 From: "Christoph \"criztovyl\" Schulz" Date: Tue, 2 Jan 2018 00:15:11 +0100 Subject: [PATCH 10/12] Do not write comments for PDF The CSS-hover effect does not work here, so disable for now. --- src/PhpSpreadsheet/Writer/Html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index 148c226ad9..ae8c196b38 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -1411,7 +1411,7 @@ private function generateRow(Worksheet $pSheet, array $pValues, $pRow, $cellType // Taken from LibreOffice core // https://github.com/LibreOffice/core/blob/9fc9bf3240f8c62ad7859947ab8a033ac1fe93fa/sc/source/filter/html/htmlexp.cxx#L1073-L1092 - if (isset($pSheet->getComments()[$coordinate])) { + if (!$this->isPdf && isset($pSheet->getComments()[$coordinate])) { $html .= ''; $html .= '
' . nl2br($pSheet->getComment($coordinate)->getText()->getPlainText()) . '
'; $html .= PHP_EOL; From b7f1e109df1878749c5dbed5db88154322d78bd8 Mon Sep 17 00:00:00 2001 From: "Christoph \"criztovyl\" Schulz" Date: Tue, 2 Jan 2018 20:49:46 +0100 Subject: [PATCH 11/12] More test cases --- .../Functional/HtmlCommentsTest.php | 60 ++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php b/tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php index cfd8f2efe8..687fc50c5e 100644 --- a/tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php +++ b/tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php @@ -3,40 +3,60 @@ namespace PhpOffice\PhpSpreadsheetTests\Functional; use PhpOffice\PhpSpreadsheet\Spreadsheet; +use PhpOffice\PhpSpreadsheet\RichText\RichText; class HtmlCommentsTest extends AbstractFunctional { - private $value = 'I am comment.'; private $spreadsheet; - private function createComment() + public function providerCommentRichText() + { + $valueSingle = 'I am comment.'; + $valueMulti= 'I am ' . PHP_EOL . 'multi-line' . PHP_EOL . 'comment.'; + + $plainSingle = new RichText(); + $plainSingle->createText($valueSingle); + + $plainMulti = new RichText(); + $plainMulti->createText($valueMulti); + + $richSingle = new RichText(); + $richSingle->createTextRun($valueSingle)->getFont()->setBold(true); + + $richMultiSimple = new RichText(); + $richMultiSimple->createTextRun($valueMulti)->getFont()->setBold(true); + + $richMultiMixed = new RichText(); + $richMultiMixed->createText('I am' . PHP_EOL); + $richMultiMixed->createTextRun('multi-line')->getFont()->setBold(true); + $richMultiMixed->createText(PHP_EOL . 'comment!'); + + return [ + 'single line plain text' => [$plainSingle], + 'multi-line plain text' => [$plainMulti], + 'single line simple rich text' => [$richSingle], + 'multi-line simple rich text' => [$richMultiSimple], + 'multi-line mixed rich text' => [$richMultiMixed], + ]; + } + + /** + * @dataProvider providerCommentRichText + */ + public function testComments($richText) { $this->spreadsheet = new Spreadsheet(); $this->spreadsheet->getActiveSheet()->getCell('A1')->setValue('Comment'); - return $this->spreadsheet->getActiveSheet() + $this->spreadsheet->getActiveSheet() ->getComment('A1') - ->getText()->createTextRun($this->value); - } + ->setText($richText); - private function doTheTest($string) - { $reloadedSpreadsheet = $this->writeAndReload($this->spreadsheet, 'Html'); - $actual = (string) $reloadedSpreadsheet->getActiveSheet()->getComment('A1')->getText()->getPlainText(); - self::assertSame($this->value, $actual, $string); - } - - public function testPlainTextComment() - { - $this->createComment(); - $this->doTheTest('should be able to read and write plain text comments from and to html as plain text'); + $actual = $reloadedSpreadsheet->getActiveSheet()->getComment('A1')->getText()->getPlainText(); + self::assertSame($richText->getPlainText(), $actual); } - public function testRichTextComment() - { - $this->createComment()->getFont()->setBold(true); - $this->doTheTest('should be able to read and write rich text comments from and to html as plain text'); - } } From eb2eb5995f452a9b2f3827fa85276eb2a1deada3 Mon Sep 17 00:00:00 2001 From: "Christoph \"criztovyl\" Schulz" Date: Fri, 5 Jan 2018 18:56:47 +0100 Subject: [PATCH 12/12] Code Style --- .../PhpSpreadsheetTests/Functional/HtmlCommentsTest.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php b/tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php index 687fc50c5e..1b497277a6 100644 --- a/tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php +++ b/tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php @@ -2,8 +2,8 @@ namespace PhpOffice\PhpSpreadsheetTests\Functional; -use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\RichText\RichText; +use PhpOffice\PhpSpreadsheet\Spreadsheet; class HtmlCommentsTest extends AbstractFunctional { @@ -12,7 +12,7 @@ class HtmlCommentsTest extends AbstractFunctional public function providerCommentRichText() { $valueSingle = 'I am comment.'; - $valueMulti= 'I am ' . PHP_EOL . 'multi-line' . PHP_EOL . 'comment.'; + $valueMulti = 'I am ' . PHP_EOL . 'multi-line' . PHP_EOL . 'comment.'; $plainSingle = new RichText(); $plainSingle->createText($valueSingle); @@ -36,12 +36,14 @@ public function providerCommentRichText() 'multi-line plain text' => [$plainMulti], 'single line simple rich text' => [$richSingle], 'multi-line simple rich text' => [$richMultiSimple], - 'multi-line mixed rich text' => [$richMultiMixed], + 'multi-line mixed rich text' => [$richMultiMixed], ]; } /** * @dataProvider providerCommentRichText + * + * @param mixed $richText */ public function testComments($richText) { @@ -58,5 +60,4 @@ public function testComments($richText) $actual = $reloadedSpreadsheet->getActiveSheet()->getComment('A1')->getText()->getPlainText(); self::assertSame($richText->getPlainText(), $actual); } - }