Support for cell comments in HTML writer and reader
The behavior is similar to what is done in LibreOffice. That means if there is a comment it will be shown with a small indicator and the actual comment will be revealed when mouse hover over the indicator. Fixes #308 Closes #310
This commit is contained in:
parent
98e0a97139
commit
cdbf3347cb
|
@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Support cell comments in HTML writer and reader- [#308](https://github.com/PHPOffice/PhpSpreadsheet/issues/308)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Better auto-detection of CSV separators - [#305](https://github.com/PHPOffice/PhpSpreadsheet/issues/305)
|
- Better auto-detection of CSV separators - [#305](https://github.com/PHPOffice/PhpSpreadsheet/issues/305)
|
||||||
|
|
|
@ -1231,14 +1231,14 @@
|
||||||
<td style="text-align: center; color: orange;">●</td>
|
<td style="text-align: center; color: orange;">●</td>
|
||||||
<td style="text-align: center; color: orange;">●</td>
|
<td style="text-align: center; color: orange;">●</td>
|
||||||
<td style="text-align: center;">N/A</td>
|
<td style="text-align: center;">N/A</td>
|
||||||
<td></td>
|
<td style="text-align: center; color: orange;">● <sup>1</sup></td>
|
||||||
<td style="text-align: center;">N/A</td>
|
<td style="text-align: center;">N/A</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding-left: 1em;">Rich Text</td>
|
<td style="padding-left: 1em;">Rich Text</td>
|
||||||
<td style="text-align: center; color: red;">✖ <sup>1</sup></td>
|
<td style="text-align: center; color: red;">✖ <sup>2</sup></td>
|
||||||
<td style="text-align: center; color: green;">✔</td>
|
<td style="text-align: center; color: green;">✔</td>
|
||||||
<td style="text-align: center; color: red;">✖</td>
|
<td style="text-align: center; color: red;">✖</td>
|
||||||
<td style="text-align: center; color: red;">✖</td>
|
<td style="text-align: center; color: red;">✖</td>
|
||||||
|
@ -1256,7 +1256,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding-left: 1em;">Alignment</td>
|
<td style="padding-left: 1em;">Alignment</td>
|
||||||
<td style="text-align: center; color: red;">✖ <sup>2</sup></td>
|
<td style="text-align: center; color: red;">✖ <sup>3</sup></td>
|
||||||
<td style="text-align: center; color: red;">✖</td>
|
<td style="text-align: center; color: red;">✖</td>
|
||||||
<td style="text-align: center; color: red;">✖</td>
|
<td style="text-align: center; color: red;">✖</td>
|
||||||
<td style="text-align: center; color: red;">✖</td>
|
<td style="text-align: center; color: red;">✖</td>
|
||||||
|
@ -1568,5 +1568,6 @@
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
1. Only BIFF8 files support Rich Text. Prior to that, comments could only be plain text
|
1. Only text contents
|
||||||
2. Only BIFF8 files support alignment and rotation. Prior to that, comments could only be unformatted text
|
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
|
||||||
|
|
|
@ -312,6 +312,14 @@ class Html extends BaseReader
|
||||||
case 'em':
|
case 'em':
|
||||||
case 'strong':
|
case 'strong':
|
||||||
case 'b':
|
case 'b':
|
||||||
|
if (isset($attributeArray['class']) && $attributeArray['class'] === 'comment') {
|
||||||
|
$sheet->getComment($column . $row)
|
||||||
|
->getText()
|
||||||
|
->createTextRun($child->textContent);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if ($cellContent > '') {
|
if ($cellContent > '') {
|
||||||
$cellContent .= ' ';
|
$cellContent .= ' ';
|
||||||
}
|
}
|
||||||
|
@ -354,6 +362,10 @@ class Html extends BaseReader
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
case 'class':
|
||||||
|
if ($attributeValue === 'comment-indicator') {
|
||||||
|
break; // Ignore - it's just a red square.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$cellContent .= ' ';
|
$cellContent .= ' ';
|
||||||
|
|
|
@ -830,6 +830,25 @@ class Html extends BaseWriter
|
||||||
$css['html']['background-color'] = 'white';
|
$css['html']['background-color'] = 'white';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CSS for comments as found in LibreOffice
|
||||||
|
$css['a.comment-indicator:hover + div.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['div.comment']['display'] = 'none';
|
||||||
|
|
||||||
// table { }
|
// table { }
|
||||||
$css['table']['border-collapse'] = 'collapse';
|
$css['table']['border-collapse'] = 'collapse';
|
||||||
if (!$this->isPdf) {
|
if (!$this->isPdf) {
|
||||||
|
@ -1385,6 +1404,8 @@ class Html extends BaseWriter
|
||||||
}
|
}
|
||||||
$html .= '>';
|
$html .= '>';
|
||||||
|
|
||||||
|
$html .= $this->writeComment($pSheet, $coordinate);
|
||||||
|
|
||||||
// Image?
|
// Image?
|
||||||
$html .= $this->writeImageInCell($pSheet, $coordinate);
|
$html .= $this->writeImageInCell($pSheet, $coordinate);
|
||||||
|
|
||||||
|
@ -1646,4 +1667,26 @@ class Html extends BaseWriter
|
||||||
|
|
||||||
return "<style>\n" . $htmlPage . $htmlBody . "</style>\n";
|
return "<style>\n" . $htmlPage . $htmlBody . "</style>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a comment in the same format as LibreOffice.
|
||||||
|
*
|
||||||
|
* @see https://github.com/LibreOffice/core/blob/9fc9bf3240f8c62ad7859947ab8a033ac1fe93fa/sc/source/filter/html/htmlexp.cxx#L1073-L1092
|
||||||
|
*
|
||||||
|
* @param Worksheet $pSheet
|
||||||
|
* @param string $coordinate
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function writeComment(Worksheet $pSheet, $coordinate)
|
||||||
|
{
|
||||||
|
$result = '';
|
||||||
|
if (!$this->isPdf && isset($pSheet->getComments()[$coordinate])) {
|
||||||
|
$result .= '<a class="comment-indicator"></a>';
|
||||||
|
$result .= '<div class="comment">' . nl2br($pSheet->getComment($coordinate)->getText()->getPlainText()) . '</div>';
|
||||||
|
$result .= PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Functional;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
|
||||||
|
class HtmlCommentsTest extends AbstractFunctional
|
||||||
|
{
|
||||||
|
private $spreadsheet;
|
||||||
|
|
||||||
|
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
|
||||||
|
*
|
||||||
|
* @param mixed $richText
|
||||||
|
*/
|
||||||
|
public function testComments($richText)
|
||||||
|
{
|
||||||
|
$this->spreadsheet = new Spreadsheet();
|
||||||
|
|
||||||
|
$this->spreadsheet->getActiveSheet()->getCell('A1')->setValue('Comment');
|
||||||
|
|
||||||
|
$this->spreadsheet->getActiveSheet()
|
||||||
|
->getComment('A1')
|
||||||
|
->setText($richText);
|
||||||
|
|
||||||
|
$reloadedSpreadsheet = $this->writeAndReload($this->spreadsheet, 'Html');
|
||||||
|
|
||||||
|
$actual = $reloadedSpreadsheet->getActiveSheet()->getComment('A1')->getText()->getPlainText();
|
||||||
|
self::assertSame($richText->getPlainText(), $actual);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue