Repository: PHPOffice/PhpSpreadsheet (php)

Issue:
[Feature Request]: Ability to set built-in styles for a cell (XLSX)
This is: 

```
- [ ] a bug report
- [x] a feature request
- [x] **not** a usage question (ask them on https://stackoverflow.com/questions/tagged/phpspreadsheet or https://gitter.im/PHPOffice/PhpSpreadsheet)
```

A feature request to support setting built-in styles on a cell when writing to XLSX. We are setting 0 a.k.a Normal for all. Would be good to have ability to set from the list of built-in types e.g. Hyperlink.
`setHyperlink` is useful to set the cell's hyperlink but the styling does not reflect it.

Code reference (\PhpSpreadsheet\Reader\Xlsx\Styles.php). 
```
//    Cell Styles
       if ($this->styleXml->cellStyles) {
           foreach ($this->styleXml->cellStyles->cellStyle as $cellStyle) {
               if ((int) ($cellStyle['builtinId']) == 0) {
                   if (isset($this->cellStyles[(int) ($cellStyle['xfId'])])) {
                       // Set default style
                       $style = new Style();
                       $this->readStyle($style, $this->cellStyles[(int) ($cellStyle['xfId'])]);
                        // normal style, currently not using it for anything
                   }
               }
           }
       }
```

Essentially, one of these.
![image](https://user-images.githubusercontent.com/4538752/91635102-356f8280-ea13-11ea-8e4d-676b9cb2cb4b.png)

Thoughts?

Pull request:
Hyperlink Styles
Fix #1632. Excel automatically supplies a style for cells which contain hyperlinks (e.g. underlined, and blue text changing to purple after the link has been followed). PhpSpreadsheet cannot handle the style automatically. The user can, with some effort, specify a style for the cell which mimics Excel's choice (except for the color change after following).

Examining a sheet with a hyperlink created through Excel, it appears that Excel creates 3 different entries in styles.xml, one for cellStyleXfs, one for cellXfs, and one for cellStyles. It is difficult for me to figure out how they interrelate. This is especially so since PhpSpreadsheet outputs only 1 entry (for the default style) for each of cellStyles and cellStyleXfs. However, it appears that only the cellXfs entry is required, and, when it specifies a font whose color specifies `theme="10"` rather than an rgb value, the style works as expected.

In order to implement this, it is necessary to add a `theme` property, with setter and getter, to Style/Color. There are 12 possible values for theme, 0-11 representing 0=dk1 1=lt1 2=dk2 3=lt2 4-9=accent1-6 10=hlink 11=folHlink. This PR is mainly to allow the use of hlink, but the others are also usable if a use case arises for them. If a theme is set for Color, Xlsx Writer will use the theme rather than rgb when generating the color xml. Other writers will continue to use rgb rather than theme, so there is a use case for setting both if you want to generate both Xlsx and some other format.

The `theme` property will, for now, be ignored except for Font. There is probably a case to be made for using it for Fill, and maybe for Border and other areas that I haven't yet considered. I will wait for someone to make that case before adding those.

In order to make it as easy as possible to use this, a method `setHyperlinkTheme` is added to both Style/Color and Style/Font. The one in Color sets `theme` to the appropriate value. The one in Font calls the one in Color, and also sets `underline` on (this will be honored by other writers in addition to Xlsx).

Samples which use hyperlinks are updated to use `setHyperlinkTheme`. So is `Reader\Xlsx\HyperlinkTest`, with appropriate tests added.

This is:

- [ ] a bugfix
- [x] a new feature
- [ ] refactoring
- [ ] additional unit tests

Checklist:

- [ ] Changes are covered by unit tests
  - [ ] Changes are covered by existing unit tests
  - [x] New unit tests have been added
- [x] Code style is respected
- [ ] Commit message explains **why** the change is made (see https://github.com/erlang/otp/wiki/Writing-good-commit-messages)
- [ ] CHANGELOG.md contains a short summary of the change and a link to the pull request if applicable
- [x] Documentation is updated as necessary



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fdbc602ab..b2cc4d6c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
 - Print Area and Row Break. [Issue #1275](https://github.com/PHPOffice/PhpSpreadsheet/issues/1275) [PR #4450](https://github.com/PHPOffice/PhpSpreadsheet/pull/4450)
 - Copy Styles after insertNewColumnBefore. [Issue #1425](https://github.com/PHPOffice/PhpSpreadsheet/issues/1425) [PR #4468](https://github.com/PHPOffice/PhpSpreadsheet/pull/4468)
 - Xls Writer Treat Hyperlink Starting with # as Internal. [Issue #56](https://github.com/PHPOffice/PhpSpreadsheet/issues/56) [PR #4453](https://github.com/PHPOffice/PhpSpreadsheet/pull/4453)
+- Hyperlink Styles. [Issue #1632](https://github.com/PHPOffice/PhpSpreadsheet/issues/1632) [PR #4478](https://github.com/PHPOffice/PhpSpreadsheet/pull/4478)
 - ODS Handling of Ceiling and Floor. [Issue #477](https://github.com/PHPOffice/PhpSpreadsheet/issues/407) [PR #4466](https://github.com/PHPOffice/PhpSpreadsheet/pull/4466)
 - Xlsx Reader Do Not Process Printer Settings for Dataonly. [Issue #4477](https://github.com/PHPOffice/PhpSpreadsheet/issues/4477) [PR #4480](https://github.com/PHPOffice/PhpSpreadsheet/pull/4480)
 
diff --git a/docs/topics/recipes.md b/docs/topics/recipes.md
index 8168abb24..4772710ee 100644
--- a/docs/topics/recipes.md
+++ b/docs/topics/recipes.md
@@ -20,7 +20,7 @@ finding a specific document in a file repository or a document
 management system. For example Microsoft Sharepoint uses document
 metadata to search for a specific document in its document lists.
 
-<details>
+<details markdown>
   <summary>Click here for details of Spreadsheet Document Properties</summary>
 
 These are accessed in MS Excel from the "Info" option on the "File" menu:
@@ -67,7 +67,7 @@ $spreadsheet->getProperties()
 
 You can choose which properties to set or ignore.
 
-<details>
+<details markdown>
   <summary>Click here for details of Property Getters/Setters</summary>
 
 PhpSpreadsheet provides specific getters/setters for a number of pre-defined properties.
@@ -90,7 +90,7 @@ PhpSpreadsheet provides specific getters/setters for a number of pre-defined pro
 
 </details>
 
-<details>
+<details markdown>
   <summary>Click here for details of Custom Properties</summary>
 
 Additionally, PhpSpreadsheet supports the creation and reading of custom properties for those file formats that accept custom properties.
@@ -143,7 +143,7 @@ $spreadsheet->getProperties()
 
 A Spreadsheet consists of (very rarely) none, one or more Worksheets. If you have 1 or more Worksheets, then one (and only one) of those Worksheets can be "Active" (viewed or updated) at a time, but there will always be an "Active" Worksheet (unless you explicitly delete all of the Worksheets in the Spreadsheet).
 
-<details>
+<details markdown>
   <summary>Click here for details about Worksheets</summary>
 
 When you create a new Spreadsheet in MS Excel, it creates the Spreadsheet with a single Worksheet ("Sheet1")
@@ -278,7 +278,7 @@ define your own values as long as they are a valid MS Excel format.
 PhpSpreadsheet also provides a number of Wizards to help you create
 Date, Time and DateTime format masks.
 
-<details>
+<details markdown>
   <summary>Click here for an example of the Date/Time Wizards</summary>
 
 ```php
@@ -631,17 +631,35 @@ $spreadsheet->getActiveSheet()->getCell('A1')
 You can make a cell a clickable URL by setting its hyperlink property:
 
 ```php
-$spreadsheet->getActiveSheet()->setCellValue('E26', 'www.phpexcel.net');
-$spreadsheet->getActiveSheet()->getCell('E26')->getHyperlink()->setUrl('https://www.example.com');
+$spreadsheet->getActiveSheet()->setCellValue('E26', 'www.example.com');
+$spreadsheet->getActiveSheet()->getCell('E26')
+    ->getHyperlink()
+    ->setUrl('https://www.example.com');
 ```
 
 If you want to make a hyperlink to another worksheet/cell, use the
 following code:
 
 ```php
-$spreadsheet->getActiveSheet()->setCellValue('E26', 'www.phpexcel.net');
-$spreadsheet->getActiveSheet()->getCell('E26')->getHyperlink()->setUrl("sheet://'Sheetname'!A1");
+$spreadsheet->getActiveSheet()
+    ->setCellValue('E27', 'go to another sheet');
+$spreadsheet->getActiveSheet()->getCell('E27')
+    ->getHyperlink()
+    ->setUrl("sheet://'Sheetname'!A1");
+```
+
+Excel automatically supplies a special style when a hyperlink is
+entered into a cell. PhpSpreadsheet cannot do so. However,
+starting with release 4.3,
+you can mimic Excel's behavior with:
+```php
+$spreadsheet->getActiveSheet()
+    ->getStyle('E26')
+    ->getFont()
+    ->setHyperlinkTheme();
 ```
+This will set underline (all formats) and text color (always
+for Xlsx, and usually for other formats).
 
 ## Setting Printer Options for Excel files
 
@@ -824,7 +842,9 @@ $drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing();
 $drawing->setName('PhpSpreadsheet logo');
 $drawing->setPath('./images/PhpSpreadsheet_logo.png');
 $drawing->setHeight(36);
-$spreadsheet->getActiveSheet()->getHeaderFooter()->addImage($drawing, \PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooter::IMAGE_HEADER_LEFT);
+$spreadsheet->getActiveSheet()
+    ->getHeaderFooter()
+    ->addImage($drawing, \PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooter::IMAGE_HEADER_LEFT);
 ```
 
 ### Setting printing breaks on a row or column
@@ -2018,7 +2038,11 @@ $richText->createText('This invoice is ');
 $payable = $richText->createTextRun('payable within thirty days after the end of the month');
 $payable->getFont()->setBold(true);
 $payable->getFont()->setItalic(true);
-$payable->getFont()->setColor( new \PhpOffice\PhpSpreadsheet\Style\Color( \PhpOffice\PhpSpreadsheet\Style\Color::COLOR_DARKGREEN ) );
+$payable->getFont()->setColor(
+    new \PhpOffice\PhpSpreadsheet\Style\Color( 
+        \PhpOffice\PhpSpreadsheet\Style\Color::COLOR_DARKGREEN
+    )
+);
 $richText->createText(', unless specified otherwise on the invoice.');
 $spreadsheet->getActiveSheet()->getCell('A18')->setValue($richText);
 ```
diff --git a/mkdocs.yml b/mkdocs.yml
index f79acb69c..d382c6385 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -7,3 +7,5 @@ extra_css:
     - extra/extra.css
 extra_javascript:
     - extra/extrajs.js
+markdown_extensions:
+    - md_in_html
diff --git a/samples/Basic/02_Types.php b/samples/Basic/02_Types.php
index 865b1d267..caf6d7a1c 100644
--- a/samples/Basic/02_Types.php
+++ b/samples/Basic/02_Types.php
@@ -142,9 +142,11 @@ $spreadsheet->getActiveSheet()
     ->getHyperlink()
     ->setUrl('https://github.com/PHPOffice/PhpSpreadsheet')
     ->setTooltip('Navigate to PhpSpreadsheet website');
+$spreadsheet->getActiveSheet()->getStyle('C17')->getFont()->setHyperlinkTheme();
 
 $spreadsheet->getActiveSheet()
     ->setCellValue('C18', '=HYPERLINK("mailto:abc@def.com","abc@def.com")');
+$spreadsheet->getActiveSheet()->getStyle('C18')->getFont()->setHyperlinkTheme();
 
 $spreadsheet->getActiveSheet()
     ->setCellValue('A20', 'String')
diff --git a/samples/templates/sampleSpreadsheet.php b/samples/templates/sampleSpreadsheet.php
index 2f30139ee..656b3ed54 100644
--- a/samples/templates/sampleSpreadsheet.php
+++ b/samples/templates/sampleSpreadsheet.php
@@ -248,16 +248,18 @@ $spreadsheet->getActiveSheet()->getStyle('B1')->getProtection()->setLocked(Prote
 
 // Add a hyperlink to the sheet
 $helper->log('Add a hyperlink to an external website');
-$spreadsheet->getActiveSheet()->setCellValue('E26', 'www.phpexcel.net');
+$spreadsheet->getActiveSheet()->setCellValue('E26', 'www.example.com');
 $spreadsheet->getActiveSheet()->getCell('E26')->getHyperlink()->setUrl('https://www.example.com');
 $spreadsheet->getActiveSheet()->getCell('E26')->getHyperlink()->setTooltip('Navigate to website');
 $spreadsheet->getActiveSheet()->getStyle('E26')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
+$spreadsheet->getActiveSheet()->getStyle('E26')->getFont()->setHyperlinkTheme();
 
 $helper->log('Add a hyperlink to another cell on a different worksheet within the workbook');
 $spreadsheet->getActiveSheet()->setCellValue('E27', 'Terms and conditions');
 $spreadsheet->getActiveSheet()->getCell('E27')->getHyperlink()->setUrl("sheet://'Terms and conditions'!A1");
 $spreadsheet->getActiveSheet()->getCell('E27')->getHyperlink()->setTooltip('Review terms and conditions');
 $spreadsheet->getActiveSheet()->getStyle('E27')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
+$spreadsheet->getActiveSheet()->getStyle('E27')->getFont()->setHyperlinkTheme();
 
 // Add a drawing to the worksheet
 $helper->log('Add a drawing to the worksheet');
diff --git a/samples/templates/sampleSpreadsheet2.php b/samples/templates/sampleSpreadsheet2.php
index f6a43820b..13184a56b 100644
--- a/samples/templates/sampleSpreadsheet2.php
+++ b/samples/templates/sampleSpreadsheet2.php
@@ -248,16 +248,18 @@ $spreadsheet->getActiveSheet()->getStyle('B1')->getProtection()->setLocked(Prote
 
 // Add a hyperlink to the sheet
 $helper->log('Add a hyperlink to an external website');
-$spreadsheet->getActiveSheet()->setCellValue('E26', 'www.phpexcel.net');
+$spreadsheet->getActiveSheet()->setCellValue('E26', 'www.example.com');
 $spreadsheet->getActiveSheet()->getCell('E26')->getHyperlink()->setUrl('https://www.example.com');
 $spreadsheet->getActiveSheet()->getCell('E26')->getHyperlink()->setTooltip('Navigate to website');
 $spreadsheet->getActiveSheet()->getStyle('E26')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
+$spreadsheet->getActiveSheet()->getStyle('E26')->getFont()->setHyperlinkTheme();
 
 $helper->log('Add a hyperlink to another cell on a different worksheet within the workbook');
 $spreadsheet->getActiveSheet()->setCellValue('E27', 'Terms and conditions');
 $spreadsheet->getActiveSheet()->getCell('E27')->getHyperlink()->setUrl("sheet://'Terms and conditions'!A1");
 $spreadsheet->getActiveSheet()->getCell('E27')->getHyperlink()->setTooltip('Review terms and conditions');
 $spreadsheet->getActiveSheet()->getStyle('E27')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
+$spreadsheet->getActiveSheet()->getStyle('E27')->getFont()->setHyperlinkTheme();
 
 // Add a drawing to the worksheet
 $helper->log('Add a drawing to the worksheet');
diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Styles.php b/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
index 7dce244db..662da9933 100644
--- a/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
+++ b/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
@@ -104,7 +104,14 @@ class Styles extends BaseParserClass
             $attr = $this->getStyleAttributes($fontStyleXml->strike);
             $fontStyle->setStrikethrough(!isset($attr['val']) || self::boolean((string) $attr['val']));
         }
-        $fontStyle->getColor()->setARGB($this->readColor($fontStyleXml->color));
+        $fontStyle->getColor()
+            ->setARGB(
+                $this->readColor($fontStyleXml->color)
+            );
+        $theme = $this->readColorTheme($fontStyleXml->color);
+        if ($theme >= 0) {
+            $fontStyle->getColor()->setTheme($theme);
+        }
 
         if (isset($fontStyleXml->u)) {
             $attr = $this->getStyleAttributes($fontStyleXml->u);
@@ -398,6 +405,17 @@ class Styles extends BaseParserClass
         }
     }
 
+    public function readColorTheme(SimpleXMLElement $color): int
+    {
+        $attr = $this->getStyleAttributes($color);
+        $retVal = -1;
+        if (isset($attr['theme']) && is_numeric((string) $attr['theme']) && !isset($attr['tint'])) {
+            $retVal = (int) $attr['theme'];
+        }
+
+        return $retVal;
+    }
+
     public function readColor(SimpleXMLElement $color, bool $background = false): string
     {
         $attr = $this->getStyleAttributes($color);
diff --git a/src/PhpSpreadsheet/Style/Color.php b/src/PhpSpreadsheet/Style/Color.php
index f48989e02..10929bc0f 100644
--- a/src/PhpSpreadsheet/Style/Color.php
+++ b/src/PhpSpreadsheet/Style/Color.php
@@ -2,6 +2,8 @@
 
 namespace PhpOffice\PhpSpreadsheet\Style;
 
+use PhpOffice\PhpSpreadsheet\Theme;
+
 class Color extends Supervisor
 {
     const NAMED_COLORS = [
@@ -111,6 +113,8 @@ class Color extends Supervisor
 
     private bool $hasChanged = false;
 
+    private int $theme = -1;
+
     /**
      * Create a new Color.
      *
@@ -176,14 +180,18 @@ class Color extends Supervisor
      * $spreadsheet->getActiveSheet()->getStyle('B2')->getFont()->getColor()->applyFromArray(['rgb' => '808080']);
      * </code>
      *
-     * @param array{rgb?: string, argb?: string} $styleArray Array containing style information
+     * @param array{rgb?: string, argb?: string, theme?: int} $styleArray Array containing style information
      *
      * @return $this
      */
     public function applyFromArray(array $styleArray): static
     {
         if ($this->isSupervisor) {
-            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
+            $this->getActiveSheet()
+                ->getStyle($this->getSelectedCells())
+                ->applyFromArray(
+                    $this->getStyleArray($styleArray)
+                );
         } else {
             if (isset($styleArray['rgb'])) {
                 $this->setRGB($styleArray['rgb']);
@@ -191,6 +199,9 @@ class Color extends Supervisor
             if (isset($styleArray['argb'])) {
                 $this->setARGB($styleArray['argb']);
             }
+            if (isset($styleArray['theme'])) {
+                $this->setTheme($styleArray['theme']);
+            }
         }
 
         return $this;
@@ -237,6 +248,7 @@ class Color extends Supervisor
     public function setARGB(?string $colorValue = self::COLOR_BLACK, bool $nullStringOkay = false): static
     {
         $this->hasChanged = true;
+        $this->setTheme(-1);
         if (!$nullStringOkay || $colorValue !== '') {
             $colorValue = $this->validateColor($colorValue);
             if ($colorValue === '') {
@@ -402,6 +414,7 @@ class Color extends Supervisor
 
         return md5(
             $this->argb
+            . (string) $this->theme
             . __CLASS__
         );
     }
@@ -411,6 +424,7 @@ class Color extends Supervisor
     {
         $exportedArray = [];
         $this->exportArray2($exportedArray, 'argb', $this->getARGB());
+        $this->exportArray2($exportedArray, 'theme', $this->getTheme());
 
         return $exportedArray;
     }
@@ -423,4 +437,42 @@ class Color extends Supervisor
 
         return $this->hasChanged;
     }
+
+    public function getTheme(): int
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getTheme();
+        }
+
+        return $this->theme;
+    }
+
+    public function setTheme(int $theme): self
+    {
+        $this->hasChanged = true;
+
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['theme' => $theme]);
+            $this->getActiveSheet()
+                ->getStyle($this->getSelectedCells())
+                ->applyFromArray($styleArray);
+        } else {
+            $this->theme = $theme;
+        }
+
+        return $this;
+    }
+
+    public function setHyperlinkTheme(): self
+    {
+        $rgb = $this->getActiveSheet()
+            ->getParent()
+            ?->getTheme()
+            ->getThemeColors();
+        if (is_array($rgb) && array_key_exists('hlink', $rgb)) {
+            $this->setRGB($rgb['hlink']);
+        }
+
+        return $this->setTheme(Theme::HYPERLINK_THEME);
+    }
 }
diff --git a/src/PhpSpreadsheet/Style/Font.php b/src/PhpSpreadsheet/Style/Font.php
index 5a74f2b6f..ade8e7ead 100644
--- a/src/PhpSpreadsheet/Style/Font.php
+++ b/src/PhpSpreadsheet/Style/Font.php
@@ -206,7 +206,10 @@ class Font extends Supervisor
                 $this->setStrikethrough($styleArray['strikethrough']);
             }
             if (isset($styleArray['color'])) {
-                $this->getColor()->applyFromArray($styleArray['color']);
+                /** @var array{rgb?: string, argb?: string, theme?: int} */
+                $temp = $styleArray['color'];
+                $this->getColor()
+                    ->applyFromArray($temp);
             }
             if (isset($styleArray['size'])) {
                 $this->setSize($styleArray['size']);
@@ -398,9 +401,6 @@ class Font extends Supervisor
      */
     public function setBold(bool $bold): static
     {
-        if ($bold == '') {
-            $bold = false;
-        }
         if ($this->isSupervisor) {
             $styleArray = $this->getStyleArray(['bold' => $bold]);
             $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
@@ -430,9 +430,6 @@ class Font extends Supervisor
      */
     public function setItalic(bool $italic): static
     {
-        if ($italic == '') {
-            $italic = false;
-        }
         if ($this->isSupervisor) {
             $styleArray = $this->getStyleArray(['italic' => $italic]);
             $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
@@ -669,10 +666,6 @@ class Font extends Supervisor
      */
     public function setStrikethrough(bool $strikethru): static
     {
-        if ($strikethru == '') {
-            $strikethru = false;
-        }
-
         if ($this->isSupervisor) {
             $styleArray = $this->getStyleArray(['strikethrough' => $strikethru]);
             $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
@@ -830,6 +823,14 @@ class Font extends Supervisor
         return $this->cap;
     }
 
+    public function setHyperlinkTheme(): self
+    {
+        $this->color->setHyperlinkTheme();
+        $this->setUnderline(self::UNDERLINE_SINGLE);
+
+        return $this;
+    }
+
     /**
      * Implement PHP __clone to create a deep clone, not just a shallow copy.
      */
diff --git a/src/PhpSpreadsheet/Theme.php b/src/PhpSpreadsheet/Theme.php
index 2366fd672..91cd84485 100644
--- a/src/PhpSpreadsheet/Theme.php
+++ b/src/PhpSpreadsheet/Theme.php
@@ -8,6 +8,7 @@ class Theme
 
     private string $themeFontName = 'Office';
 
+    public const HYPERLINK_THEME = 10;
     public const COLOR_SCHEME_2013_PLUS_NAME = 'Office 2013+';
     public const COLOR_SCHEME_2013_PLUS = [
         'dk1' => '000000',
diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Style.php b/src/PhpSpreadsheet/Writer/Xlsx/Style.php
index 8b09dd66c..bd7a56c82 100644
--- a/src/PhpSpreadsheet/Writer/Xlsx/Style.php
+++ b/src/PhpSpreadsheet/Writer/Xlsx/Style.php
@@ -347,7 +347,12 @@ class Style extends WriterPart
         }
 
         // Foreground color
-        if ($font->getColor()->getARGB() !== null) {
+        if ($font->getColor()->getTheme() >= 0) {
+            $this->startFont($objWriter, $fontStarted);
+            $objWriter->startElement('color');
+            $objWriter->writeAttribute('theme', (string) $font->getColor()->getTheme());
+            $objWriter->endElement();
+        } elseif ($font->getColor()->getARGB() !== null) {
             $this->startFont($objWriter, $fontStarted);
             $objWriter->startElement('color');
             $objWriter->writeAttribute('rgb', $font->getColor()->getARGB());


Test patch:
diff --git a/tests/PhpSpreadsheetTests/Functional/CommentsTest.php b/tests/PhpSpreadsheetTests/Functional/CommentsTest.php
index c92f1c4e1..4215b7a14 100644
--- a/tests/PhpSpreadsheetTests/Functional/CommentsTest.php
+++ b/tests/PhpSpreadsheetTests/Functional/CommentsTest.php
@@ -48,10 +48,10 @@ class CommentsTest extends AbstractFunctional
         self::assertEquals($comment, $commentClone);
         self::assertNotSame($comment, $commentClone);
         if ($format === 'Xlsx') {
-            self::assertEquals('feb0c24b880a8130262dadf801f85e94', $comment->getHashCode());
-            self::assertEquals(Alignment::HORIZONTAL_GENERAL, $comment->getAlignment());
+            self::assertSame('bc7bcec8f676a333dae65c945cf8dace', $comment->getHashCode(), 'changed due to addition of theme to fillColor');
+            self::assertSame(Alignment::HORIZONTAL_GENERAL, $comment->getAlignment());
             $comment->setAlignment(Alignment::HORIZONTAL_RIGHT);
-            self::assertEquals(Alignment::HORIZONTAL_RIGHT, $comment->getAlignment());
+            self::assertSame(Alignment::HORIZONTAL_RIGHT, $comment->getAlignment());
         }
         $spreadsheet->disconnectWorksheets();
         $reloadedSpreadsheet->disconnectWorksheets();
diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/HyperlinkTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/HyperlinkTest.php
index 81bc5e178..5e2748fef 100644
--- a/tests/PhpSpreadsheetTests/Reader/Xlsx/HyperlinkTest.php
+++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/HyperlinkTest.php
@@ -19,36 +19,80 @@ class HyperlinkTest extends AbstractFunctional
         $sheet1->getCell('B1')->setValue('this is b1');
         $spreadsheet->addNamedRange(new NamedRange('namedb1', $sheet1, '$B$1'));
         $sheet1->setCellValue('A2', 'link to same sheet');
-        $sheet1->getCell('A2')->getHyperlink()->setUrl("sheet://'Sheet One'!A1");
+        $sheet1->getCell('A2')->getHyperlink()
+            ->setUrl("sheet://'Sheet One'!A1");
+        $sheet1->getStyle('A2')->getFont()->setHyperlinkTheme();
         $sheet1->setCellValue('A3', 'link to defined name');
-        $sheet1->getCell('A3')->getHyperlink()->setUrl('sheet://namedb1');
+        $sheet1->getCell('A3')->getHyperlink()
+            ->setUrl('sheet://namedb1');
+        $sheet1->getStyle('A3')->getFont()->setHyperlinkTheme();
 
         $sheet2 = $spreadsheet->createSheet();
         $sheet2->setTitle('Sheet Two');
         $sheet2->setCellValue('A2', 'link to other sheet');
-        $sheet2->getCell('A2')->getHyperlink()->setUrl("sheet://'Sheet One'!A1");
+        $sheet2->getCell('A2')->getHyperlink()
+            ->setUrl("sheet://'Sheet One'!A1");
+        $sheet2->getStyle('A2')->getFont()->setHyperlinkTheme();
         $sheet2->setCellValue('A3', 'external link');
-        $sheet2->getCell('A3')->getHyperlink()->setUrl('https://www.example.com');
+        $sheet2->getCell('A3')->getHyperlink()
+            ->setUrl('https://www.example.com');
+        $sheet2->getStyle('A3')->getFont()->setHyperlinkTheme();
         $sheet2->setCellValue('A4', 'external link with anchor');
-        $sheet2->getCell('A4')->getHyperlink()->setUrl('https://www.example.com#anchor');
+        $sheet2->getCell('A4')->getHyperlink()
+            ->setUrl('https://www.example.com#anchor');
         $sheet2->getCell('A4')->getHyperlink()->setTooltip('go to anchor tag on example.com');
+        $sheet2->getStyle('A4')->getFont()->setHyperlinkTheme();
 
         $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
         $spreadsheet->disconnectWorksheets();
         $rsheet1 = $reloadedSpreadsheet->getSheet(0);
         self::assertSame('link to same sheet', $rsheet1->getCell('A2')->getValue());
         self::assertSame("sheet://'Sheet One'!A1", $rsheet1->getCell('A2')->getHyperlink()->getUrl());
+
         self::assertSame('link to defined name', $rsheet1->getCell('A3')->getValue());
         self::assertSame('sheet://namedb1', $rsheet1->getCell('A3')->getHyperlink()->getUrl());
 
         $rsheet2 = $reloadedSpreadsheet->getSheet(1);
         self::assertSame('link to other sheet', $rsheet2->getCell('A2')->getValue());
         self::assertSame("sheet://'Sheet One'!A1", $rsheet2->getCell('A2')->getHyperlink()->getUrl());
+         self::assertSame(
+             'FF0000FF',
+             $rsheet2
+                 ->getStyle('A2')
+                 ->getFont()->getColor()->getARGB(),
+             'argb is set in addition to theme'
+         );
+
         self::assertSame('external link', $rsheet2->getCell('A3')->getValue());
         self::assertSame('https://www.example.com', $rsheet2->getCell('A3')->getHyperlink()->getUrl());
+
         self::assertSame('https://www.example.com#anchor', $rsheet2->getCell('A4')->getHyperlink()->getUrl());
         self::assertSame('external link with anchor', $rsheet2->getCell('A4')->getValue());
         self::assertSame('go to anchor tag on example.com', $rsheet2->getCell('A4')->getHyperlink()->getToolTip());
+
+        $testCells = [
+            [0, 'A2'],
+            [0, 'A3'],
+            [0, 'A2'],
+            [1, 'A3'],
+            [1, 'A4'],
+        ];
+        foreach ($testCells as $sheetAndCell) {
+            [$sheetIndex, $cell] = $sheetAndCell;
+            $rsheet = $reloadedSpreadsheet->getSheet($sheetIndex);
+            self::assertSame(
+                10,
+                $rsheet->getStyle($cell)
+                    ->getFont()->getColor()->getTheme(),
+                "theme sheet $sheetIndex cell $cell"
+            );
+            self::assertSame(
+                'single',
+                $rsheet->getStyle('A2')->getFont()->getUnderline(),
+                "underline sheet $sheetIndex cell $cell"
+            );
+        }
+
         $reloadedSpreadsheet->disconnectWorksheets();
     }
 }
diff --git a/tests/PhpSpreadsheetTests/Style/ExportArrayTest.php b/tests/PhpSpreadsheetTests/Style/ExportArrayTest.php
index b6ab46cc0..af05cb781 100644
--- a/tests/PhpSpreadsheetTests/Style/ExportArrayTest.php
+++ b/tests/PhpSpreadsheetTests/Style/ExportArrayTest.php
@@ -179,15 +179,17 @@ class ExportArrayTest extends TestCase
 
         $cell1 = $sheet->getCell('A2');
         $cell1style = $cell1->getStyle();
-        $cell1style->getFill()->setFillType(Fill::FILL_PATTERN_GRAY125);
-        $cell1style->getFill()->getStartColor()->setArgb('FF112233');
+        $cell1style->getFill()
+            ->setFillType(Fill::FILL_PATTERN_GRAY125);
+        $cell1style->getFill()->getStartColor()
+            ->setArgb('FF112233');
         $styleArray = $cell1style->exportArray();
         self::assertEquals(
             [
                 'fillType' => Fill::FILL_PATTERN_GRAY125,
                 'rotation' => 0.0,
-                'endColor' => ['argb' => 'FF000000'],
-                'startColor' => ['argb' => 'FF112233'],
+                'endColor' => ['argb' => 'FF000000', 'theme' => -1],
+                'startColor' => ['argb' => 'FF112233', 'theme' => -1],
             ],
             $styleArray['fill'],
             'changed start color with setArgb'
@@ -208,15 +210,16 @@ class ExportArrayTest extends TestCase
 
         $cell1 = $sheet->getCell('A3');
         $cell1style = $cell1->getStyle();
-        $cell1style->getFill()->setFillType(Fill::FILL_PATTERN_GRAY125);
+        $cell1style->getFill()
+            ->setFillType(Fill::FILL_PATTERN_GRAY125);
         $cell1style->getFill()->getEndColor()->setArgb('FF112233');
         $styleArray = $cell1style->exportArray();
         self::assertEquals(
             [
                 'fillType' => Fill::FILL_PATTERN_GRAY125,
                 'rotation' => 0.0,
-                'endColor' => ['argb' => 'FF112233'],
-                'startColor' => ['argb' => 'FFFFFFFF'],
+                'endColor' => ['argb' => 'FF112233', 'theme' => -1],
+                'startColor' => ['argb' => 'FFFFFFFF', 'theme' => -1],
             ],
             $styleArray['fill'],
             'changed end color with setArgb'
@@ -224,15 +227,16 @@ class ExportArrayTest extends TestCase
 
         $cell1 = $sheet->getCell('A4');
         $cell1style = $cell1->getStyle();
-        $cell1style->getFill()->setFillType(Fill::FILL_PATTERN_GRAY125);
+        $cell1style->getFill()
+            ->setFillType(Fill::FILL_PATTERN_GRAY125);
         $cell1style->getFill()->setEndColor(new Color('FF0000FF'));
         $styleArray = $cell1style->exportArray();
         self::assertEquals(
             [
                 'fillType' => Fill::FILL_PATTERN_GRAY125,
                 'rotation' => 0.0,
-                'endColor' => ['argb' => 'FF0000FF'],
-                'startColor' => ['argb' => 'FFFFFFFF'],
+                'endColor' => ['argb' => 'FF0000FF', 'theme' => -1],
+                'startColor' => ['argb' => 'FFFFFFFF', 'theme' => -1],
             ],
             $styleArray['fill'],
             'changed end color with setEndColor'
@@ -241,14 +245,15 @@ class ExportArrayTest extends TestCase
         $cell1 = $sheet->getCell('A5');
         $cell1style = $cell1->getStyle();
         $cell1style->getFill()->setFillType(Fill::FILL_PATTERN_GRAY125);
-        $cell1style->getFill()->setStartColor(new Color('FF0000FF'));
+        $cell1style->getFill()
+            ->setStartColor(new Color('FF0000FF'));
         $styleArray = $cell1style->exportArray();
         self::assertEquals(
             [
                 'fillType' => Fill::FILL_PATTERN_GRAY125,
                 'rotation' => 0.0,
-                'startColor' => ['argb' => 'FF0000FF'],
-                'endColor' => ['argb' => 'FF000000'],
+                'startColor' => ['argb' => 'FF0000FF', 'theme' => -1],
+                'endColor' => ['argb' => 'FF000000', 'theme' => -1],
             ],
             $styleArray['fill'],
             'changed start color with setStartColor'
@@ -259,7 +264,7 @@ class ExportArrayTest extends TestCase
             [
                 'fillType' => Fill::FILL_PATTERN_GRAY125,
                 'rotation' => 45.0,
-                'startColor' => ['argb' => 'FF00FFFF'],
+                'startColor' => ['argb' => 'FF00FFFF', 'theme' => -1],
             ]
         );
         $cell1style = $cell1->getStyle();
@@ -268,8 +273,8 @@ class ExportArrayTest extends TestCase
             [
                 'fillType' => Fill::FILL_PATTERN_GRAY125,
                 'rotation' => 45.0,
-                'startColor' => ['argb' => 'FF00FFFF'],
-                'endColor' => ['argb' => 'FF000000'],
+                'startColor' => ['argb' => 'FF00FFFF', 'theme' => -1],
+                'endColor' => ['argb' => 'FF000000', 'theme' => -1],
             ],
             $styleArray['fill'],
             'applyFromArray with startColor'