Skip to content
Open
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
55 changes: 55 additions & 0 deletions src/PhpSpreadsheet/Comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ class Comment implements IComparable, Stringable
*/
private Color $fillColor;

/**
* Comment border color.
*/
private Color $borderColor;

/**
* Comment fill opacity.
*/
private float $fillOpacity = 1.0;

/**
* Comment shapeType.
*/
private int $shapeType = 202;

/**
* Alignment.
*/
Expand Down Expand Up @@ -80,6 +95,7 @@ public function __construct()
$this->author = 'Author';
$this->text = new RichText();
$this->fillColor = new Color('FFFFFFE1');
$this->borderColor = new Color(Color::COLOR_BLACK);
$this->alignment = Alignment::HORIZONTAL_GENERAL;
$this->backgroundImage = new Drawing();
}
Expand Down Expand Up @@ -240,6 +256,42 @@ public function getFillColor(): Color
return $this->fillColor;
}

public function setBorderColor(Color $color): self
{
$this->borderColor = $color;

return $this;
}

public function getBorderColor(): Color
{
return $this->borderColor;
}

public function setFillOpacity(float $opacity): self
{
$this->fillOpacity = $opacity;

return $this;
}

public function getFillOpacity(): float
{
return $this->fillOpacity;
}

public function getShapeType(): int
{
return $this->shapeType;
}

public function setShapeType(int $shapeType): self
{
$this->shapeType = $shapeType;

return $this;
}

public function setAlignment(string $alignment): self
{
$this->alignment = $alignment;
Expand Down Expand Up @@ -278,6 +330,9 @@ public function getHashCode(): string
. $this->marginTop
. ($this->visible ? 1 : 0)
. $this->fillColor->getHashCode()
. $this->borderColor->getHashCode()
. $this->fillOpacity
. $this->shapeType
. $this->alignment
. $this->textboxDirection
. ($this->hasBackgroundImage() ? $this->backgroundImage->getHashCode() : '')
Expand Down
34 changes: 34 additions & 0 deletions src/PhpSpreadsheet/Reader/Xlsx.php
Original file line number Diff line number Diff line change
Expand Up @@ -1269,11 +1269,29 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
if (isset($shape['style'])) {
$style = (string) $shape['style'];
$fillColor = strtoupper(substr((string) $shape['fillcolor'], 1));
$strokeColor = strtoupper(substr((string) $shape['strokecolor'], 1));
$column = null;
$row = null;
$textHAlign = null;
$fillImageRelId = null;
$fillImageTitle = '';
$fillOpacity = null;
$shapeType = null;

// Extract shape type from type attribute (e.g., #_x0000_t203 -> 203)
$typeAttr = (string) $shape['type'];
if (Preg::isMatch('/#_x0000_t(\d+)$/', $typeAttr, $matches)) {
$shapeType = (int) $matches[1];
}

// Extract fill opacity from v:fill element
$fillNode = $shape->xpath('.//v:fill');
if (is_array($fillNode) && !empty($fillNode)) {
$fillAttributes = $fillNode[0]->attributes();
if (isset($fillAttributes['opacity'])) {
$fillOpacity = (float) $fillAttributes['opacity'];
}
}

$clientData = $shape->xpath('.//x:ClientData');
$textboxDirection = '';
Expand Down Expand Up @@ -1338,6 +1356,22 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
// Set comment properties
$comment = $docSheet->getComment([(int) $column + 1, (int) $row + 1]);
$comment->getFillColor()->setRGB($fillColor);

// Set border color
if (!empty($strokeColor)) {
$comment->getBorderColor()->setRGB($strokeColor);
}

// Set fill opacity
if ($fillOpacity !== null) {
$comment->setFillOpacity($fillOpacity);
}

// Set shape type
if ($shapeType !== null) {
$comment->setShapeType($shapeType);
}

if (isset($fillImageRelId, $drowingImages[$fillImageRelId])) {
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$objDrawing->setName($fillImageTitle);
Expand Down
8 changes: 6 additions & 2 deletions src/PhpSpreadsheet/Writer/Xlsx/Comments.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ private function writeComment(XMLWriter $objWriter, string $cellReference, Comme

// text
$objWriter->startElement('text');
$this->getParentWriter()->getWriterPartstringtable()->writeRichText($objWriter, $comment->getText());
$this->getParentWriter()->getWriterPartStringTable()->writeRichText($objWriter, $comment->getText());
$objWriter->endElement();

$objWriter->endElement();
Expand Down Expand Up @@ -180,14 +180,18 @@ private function writeVMLComment(XMLWriter $objWriter, string $cellReference, Co
// v:shape
$objWriter->startElement('v:shape');
$objWriter->writeAttribute('id', '_x0000_s' . $id);
$objWriter->writeAttribute('type', '#_x0000_t202');
$objWriter->writeAttribute('type', '#_x0000_t' . $comment->getShapeType());
$objWriter->writeAttribute('style', 'position:absolute;margin-left:' . $comment->getMarginLeft() . ';margin-top:' . $comment->getMarginTop() . ';width:' . $comment->getWidth() . ';height:' . $comment->getHeight() . ';z-index:1;visibility:' . ($comment->getVisible() ? 'visible' : 'hidden'));
$objWriter->writeAttribute('fillcolor', '#' . $comment->getFillColor()->getRGB());
$objWriter->writeAttribute('strokecolor', '#' . $comment->getBorderColor()->getRGB());
$objWriter->writeAttribute('o:insetmode', 'auto');

// v:fill
$objWriter->startElement('v:fill');
$objWriter->writeAttribute('color2', '#' . $comment->getFillColor()->getRGB());
if ($comment->getFillOpacity() < 1.0) {
$objWriter->writeAttribute('opacity', (string) $comment->getFillOpacity());
}
if ($comment->hasBackgroundImage()) {
$bgImage = $comment->getBackgroundImage();
$objWriter->writeAttribute('o:relid', 'rId' . $bgImage->getImageIndex());
Expand Down
2 changes: 1 addition & 1 deletion tests/PhpSpreadsheetTests/Functional/CommentsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function testComments(string $format): void
self::assertEquals($comment, $commentClone);
self::assertNotSame($comment, $commentClone);
if ($format === 'Xlsx') {
self::assertSame('bc7bcec8f676a333dae65c945cf8dace', $comment->getHashCode(), 'changed due to addition of theme to fillColor');
self::assertSame('db7d24e39957b75ccd95b1bd5c7f488d', $comment->getHashCode(), 'changed due to addition of theme to fillColor');
self::assertSame(Alignment::HORIZONTAL_GENERAL, $comment->getAlignment());
$comment->setAlignment(Alignment::HORIZONTAL_RIGHT);
self::assertSame(Alignment::HORIZONTAL_RIGHT, $comment->getAlignment());
Expand Down
128 changes: 128 additions & 0 deletions tests/PhpSpreadsheetTests/Writer/Xlsx/Issue1790Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;

use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PHPUnit\Framework\TestCase;
use ZipArchive;

class Issue1790Test extends TestCase
{
public function testCommentStylingOutput(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$comment = $sheet->getComment('A1');

$comment->getFillColor()->setRGB('FF5733');
$comment->setBorderColor(new Color('0000FF'));
$comment->setFillOpacity(0.6);
$comment->setShapeType(203);

$writer = new Xlsx($spreadsheet);
$tempFile = tempnam(sys_get_temp_dir(), 'xlsx');
if ($tempFile === false) {
self::fail('Could not create temporary file');
}
$writer->save($tempFile);

$zip = new ZipArchive();
$zip->open($tempFile);
$vml = $zip->getFromName('xl/drawings/vmlDrawing1.vml');
$zip->close();
unlink($tempFile);

self::assertIsString($vml);

self::assertStringContainsString('fillcolor="#FF5733"', $vml);
self::assertStringContainsString('strokecolor="#0000FF"', $vml);
self::assertStringContainsString('opacity="0.6"', $vml);
self::assertStringContainsString('type="#_x0000_t203"', $vml);
}

public function testCommentStylingRoundTrip(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();

$comment = $sheet->getComment('A1');
$comment->getFillColor()->setRGB('FF5733');
$comment->setBorderColor(new Color('0000FF'));
$comment->setFillOpacity(0.6);
$comment->setShapeType(203);
$comment->setAuthor('TestAuthor');
$comment->getText()->createText('Test comment text');

$writer = new Xlsx($spreadsheet);
$tempFile = tempnam(sys_get_temp_dir(), 'xlsx');
if ($tempFile === false) {
self::fail('Could not create temporary file');
}
$writer->save($tempFile);

$reader = new XlsxReader();
$loadedSpreadsheet = $reader->load($tempFile);
$loadedComment = $loadedSpreadsheet->getActiveSheet()->getComment('A1');

self::assertEquals('FF5733', $loadedComment->getFillColor()->getRGB());
self::assertEquals('0000FF', $loadedComment->getBorderColor()->getRGB());
self::assertEquals(0.6, $loadedComment->getFillOpacity());
self::assertEquals(203, $loadedComment->getShapeType());
self::assertEquals('TestAuthor', $loadedComment->getAuthor());
self::assertEquals('Test comment text', $loadedComment->getText()->getPlainText());

unlink($tempFile);
$spreadsheet->disconnectWorksheets();
$loadedSpreadsheet->disconnectWorksheets();
}

public function testMultipleCommentStyling(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();

$comment1 = $sheet->getComment('A1');
$comment1->getFillColor()->setRGB('FF0000');
$comment1->setBorderColor(new Color('00FF00'));
$comment1->setFillOpacity(0.3);
$comment1->setShapeType(202);
$comment1->getText()->createText('Red comment');

$comment2 = $sheet->getComment('B2');
$comment2->getFillColor()->setRGB('0000FF');
$comment2->setBorderColor(new Color('FFFF00'));
$comment2->setFillOpacity(0.8);
$comment2->setShapeType(204);
$comment2->getText()->createText('Blue comment');

$writer = new Xlsx($spreadsheet);
$tempFile = tempnam(sys_get_temp_dir(), 'xlsx');
if ($tempFile === false) {
self::fail('Could not create temporary file');
}
$writer->save($tempFile);

$reader = new XlsxReader();
$loadedSpreadsheet = $reader->load($tempFile);
$loadedSheet = $loadedSpreadsheet->getActiveSheet();

$loadedComment1 = $loadedSheet->getComment('A1');
self::assertEquals('FF0000', $loadedComment1->getFillColor()->getRGB());
self::assertEquals('00FF00', $loadedComment1->getBorderColor()->getRGB());
self::assertEquals(0.3, $loadedComment1->getFillOpacity());
self::assertEquals(202, $loadedComment1->getShapeType());

$loadedComment2 = $loadedSheet->getComment('B2');
self::assertEquals('0000FF', $loadedComment2->getFillColor()->getRGB());
self::assertEquals('FFFF00', $loadedComment2->getBorderColor()->getRGB());
self::assertEquals(0.8, $loadedComment2->getFillOpacity());
self::assertEquals(204, $loadedComment2->getShapeType());

unlink($tempFile);
$spreadsheet->disconnectWorksheets();
$loadedSpreadsheet->disconnectWorksheets();
}
}
Loading