diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9476f34d9d844bccd738a22c7751912c450af2f9
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,22 @@
+dist: bionic
+sudo: required
+
+language: php
+
+php:
+  - 7.4.28
+
+install:
+  - composer validate --no-check-all --no-check-publish
+  - composer install
+
+script:  ./vendor/bin/phpunit Test/
+
+after_success:
+  - bash <(curl -s https://codecov.io/bash)
+
+cache:
+  apt: true
+  directories:
+    - $TRAVIS_BUILD_DIR/vendor
+    - $HOME/.composer/cache
diff --git a/Test/FilePropertyTest.php b/Test/FilePropertyTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..57c45ef63dfa4dadc63d79e8397e04808d507d48
--- /dev/null
+++ b/Test/FilePropertyTest.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Test;
+
+
+use PHPUnit\Framework\TestCase;
+
+class FilePropertyTest extends TestCase {
+
+	public function testAuthor() {
+		$expected_author = "John Doe";
+		$writer = new \XLSXWriter();
+		$writer->setAuthor($expected_author);
+
+		$xlsx_properties = $writer->getFileProperties();
+
+		$this->assertEquals($expected_author, $xlsx_properties["author"]);
+	}
+
+	public function testTitle() {
+		$expected_title = "My Spreadsheet";
+		$writer = new \XLSXWriter();
+		$writer->setTitle($expected_title);
+
+		$xlsx_properties = $writer->getFileProperties();
+
+		$this->assertEquals($expected_title, $xlsx_properties["title"]);
+	}
+
+	public function testSubject() {
+		$expected_subject = "My Spreadsheet is Wonderful";
+		$writer = new \XLSXWriter();
+		$writer->setSubject($expected_subject);
+
+		$xlsx_properties = $writer->getFileProperties();
+
+		$this->assertEquals($expected_subject, $xlsx_properties["subject"]);
+	}
+
+	public function testCompany() {
+		$expected_company = "EBANX";
+		$writer = new \XLSXWriter();
+		$writer->setCompany($expected_company);
+
+		$xlsx_properties = $writer->getFileProperties();
+
+		$this->assertEquals($expected_company, $xlsx_properties["company"]);
+	}
+
+	public function testKeywords() {
+		$expected_keywords = ["spreadsheet", "php", "EBANX"];
+		$writer = new \XLSXWriter();
+		$writer->setKeywords($expected_keywords);
+
+		$xlsx_properties = $writer->getFileProperties();
+
+		$this->assertEquals($expected_keywords, $xlsx_properties["keywords"]);
+	}
+
+}
diff --git a/Test/XLSXWriterTest.php b/Test/XLSXWriterTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..36b163640ce1f68bf129d42825305e87cd097bbc
--- /dev/null
+++ b/Test/XLSXWriterTest.php
@@ -0,0 +1,523 @@
+<?php
+
+namespace Test;
+
+include_once __DIR__.'/../vendor/autoload.php';
+
+use PHPUnit\Framework\TestCase;
+use SimpleXMLElement;
+use XLSXWriter;
+use XLSXWriter_BuffererWriter;
+use ZipArchive;
+
+class _XLSXWriter_ extends XLSXWriter
+{
+	public function writeCell(XLSXWriter_BuffererWriter &$file, $row_number, $column_number, $value, $cell_format, $cell_style_idx = null) {
+		return call_user_func_array('parent::writeCell', [&$file, $row_number, $column_number, $value, $cell_format, $cell_style_idx = null]);
+	}
+}
+//Just a simple test, by no means comprehensive
+
+class XLSXWriterTest extends TestCase
+{
+	/**
+	 * @covers XLSXWriter::writeCell
+	 */
+	public function testWriteCell() {
+		$filename = tempnam("/tmp", "xlsx_writer");
+		$file_writer = new XLSXWriter_BuffererWriter($filename);
+
+		$xlsx_writer = new _XLSXWriter_();
+		$xlsx_writer->writeCell($file_writer, 0, 0, '0123', 'string');
+		$file_writer->close();
+		$cell_xml = file_get_contents($filename);
+		$this->assertNotEquals('<c r="A1" s="0" t="n"><v>123</v></c>', $cell_xml);
+		$this->assertEquals('<c r="A1" s="0" t="s"><v>0</v></c>', $cell_xml);//0123 should be the 0th index of the shared string array
+		@unlink($filename);
+	}
+
+	/**
+	 * @covers XLSXWriter::writeToFile
+	 */
+	public function testWriteToFile() {
+		$filename = tempnam("/tmp", "xlsx_writer");
+
+		$header = ['0'=>'string','1'=>'string','2'=>'string','3'=>'string'];
+		$sheet = [
+			['55','66','77','88'],
+			['10','11','12','13'],
+		];
+
+		$xlsx_writer = new XLSXWriter();
+		$xlsx_writer->writeSheet($sheet,'mysheet',$header);
+		$xlsx_writer->writeToFile($filename);
+
+		$zip = new ZipArchive();
+		$r = $zip->open($filename);
+		$this->assertTrue($r);
+
+		$this->assertNotEmpty(($zip->numFiles));
+
+		$out_sheet = [];
+		for($z=0; $z < $zip->numFiles; $z++) {
+			$inside_zip_filename = $zip->getNameIndex($z);
+
+			if (preg_match("/sheet(\d+).xml/", basename($inside_zip_filename))) {
+				$out_sheet = $this->stripCellsFromSheetXML($zip->getFromName($inside_zip_filename));
+				array_shift($out_sheet);
+				$out_sheet = array_values($out_sheet);
+			}
+		}
+
+		$zip->close();
+		@unlink($filename);
+
+		$r1 = self::array_diff_assoc_recursive($out_sheet, $sheet);
+		$r2 = self::array_diff_assoc_recursive($sheet, $out_sheet);
+		$this->assertEmpty($r1);
+		$this->assertEmpty($r2);
+	}
+
+	public function testMarkMergedCells() {
+		$filename = tempnam("/tmp", "xlsx_writer");
+
+		$header = ['0'=>'string','1'=>'string','2'=>'string','3'=>'string'];
+		$sheet = [
+			['55','66','77','88'],
+			['10','11','12','13'],
+		];
+
+		$expected_merged_range = "B2:C3";
+
+		$xlsx_writer = new XLSXWriter();
+		$xlsx_writer->writeSheetHeader('mysheet', $header);
+		$xlsx_writer->writeSheetRow('mysheet', $sheet[0]);
+		$xlsx_writer->writeSheetRow('mysheet', $sheet[1]);
+		$xlsx_writer->markMergedCell('mysheet', 1, 1, 2, 2);
+		$xlsx_writer->writeToFile($filename);
+
+		$zip = new ZipArchive();
+		$r = $zip->open($filename);
+		$xml = $this->extractSheetXml($zip);
+
+		$this->assertTrue($r);
+		$this->assertNotEmpty(($zip->numFiles));
+		$this->assertNotEmpty($xml);
+
+		$merged_cell_range = $xml->mergeCells->mergeCell["ref"][0];
+
+		$this->assertEquals($expected_merged_range, $merged_cell_range);
+
+		$zip->close();
+		@unlink($filename);
+	}
+
+	/**
+	 * @dataProvider getFreezeCellsScenarios
+	 */
+	public function testFreezeCells($freeze_cols, $freeze_rows, $expected_active_cells, $expected_pane) {
+		$filename = tempnam("/tmp", "xlsx_writer");
+
+		$header = ['0'=>'string','1'=>'string','2'=>'string','3'=>'string'];
+		$sheet = [
+			['55','66','77','88'],
+			['10','11','12','13'],
+		];
+
+		$col_options = ['freeze_columns' => $freeze_cols, 'freeze_rows' => $freeze_rows];
+
+		$xlsx_writer = new XLSXWriter();
+		$xlsx_writer->writeSheetHeader('mysheet', $header, $format = 'xlsx', $delimiter = ';', $subheader = NULL, $col_options);
+		$xlsx_writer->writeSheetRow('mysheet', $sheet[0]);
+		$xlsx_writer->writeSheetRow('mysheet', $sheet[1]);
+		$xlsx_writer->writeToFile($filename);
+
+		$zip = new ZipArchive();
+		$r = $zip->open($filename);
+		$xml = $this->extractSheetXml($zip);
+
+		$this->assertTrue($r);
+		$this->assertNotEmpty(($zip->numFiles));
+		$this->assertNotEmpty($xml);
+
+		$sheet_view = $xml->sheetViews->sheetView;
+
+		if (!empty($expected_pane)) {
+			$pane = $sheet_view->pane;
+			foreach ($expected_pane as $expected_key => $expected_value) {
+				$attribute = (string) $pane[0][$expected_key];
+				$this->assertEquals($expected_value, $attribute);
+			}
+		}
+
+		$selections = $sheet_view->selection;
+		for ($i = 0; $i < count($expected_active_cells); $i++) {
+			$this->assertEquals($expected_active_cells[$i]['cell'], $selections[$i]['activeCell']);
+			$this->assertEquals($expected_active_cells[$i]['cell'], $selections[$i]['sqref']);
+			$this->assertEquals($expected_active_cells[$i]['pane'], $selections[$i]['pane']);
+		}
+
+		$zip->close();
+		@unlink($filename);
+	}
+
+	public static function getFreezeCellsScenarios() {
+		return [
+			"Not frozen" => [
+				$freeze_cols = false,
+				$freeze_rows = false,
+				$expected_active_cells = [["cell" => "A1", "pane" => "topLeft"]],
+				$expected_pane = [],
+			],
+			"Frozen Col B and Row 2" => [
+				$freeze_cols = 1,
+				$freeze_rows = 1,
+				$expected_active_cells = [["cell" => "A2", "pane" => "topRight"], ["cell" => "B1", "pane" => "bottomLeft"], ["cell" => "B2", "pane" => "bottomRight"]],
+				$expected_pane = ["ySplit" => $freeze_rows, "xSplit" => $freeze_cols, "topLeftCell" => "B2", "activePane" => "bottomRight"],
+			],
+			"Frozen Col B" => [
+				$freeze_cols = 1,
+				$freeze_rows = false,
+				$expected_active_cells = [["cell" => "B1", "pane" => "topRight"]],
+				$expected_pane = ["xSplit" => $freeze_cols, "topLeftCell" => "B1", "activePane" => "topRight"],
+			],
+			"Frozen Row 2" => [
+				$freeze_cols = false,
+				$freeze_rows = 1,
+				$expected_active_cells = [["cell" => "A2", "pane" => "bottomLeft"]],
+				$expected_pane = ["ySplit" => $freeze_rows, "topLeftCell" => "A2", "activePane" => "bottomLeft"],
+			],
+			"Frozen Col A and Row 1" => [
+				$freeze_cols = 0,
+				$freeze_rows = 0,
+				$expected_active_cells = [["cell" => "A1", "pane" => "topRight"], ["cell" => "A1", "pane" => "bottomLeft"], ["cell" => "A1", "pane" => "bottomRight"]],
+				$expected_pane = ["ySplit" => $freeze_rows, "xSplit" => $freeze_cols, "topLeftCell" => "A1", "activePane" => "bottomRight"],
+			],
+			"Frozen Col A" => [
+				$freeze_cols = 0,
+				$freeze_rows = false,
+				$expected_active_cells = [["cell" => "A1", "pane" => "topRight"]],
+				$expected_pane = ["xSplit" => $freeze_cols, "topLeftCell" => "A1", "activePane" => "topRight"],
+			],
+			"Frozen Row 1" => [
+				$freeze_cols = false,
+				$freeze_rows = 0,
+				$expected_active_cells = [["cell" => "A1", "pane" => "bottomLeft"]],
+				$expected_pane = ["ySplit" => $freeze_rows, "topLeftCell" => "A1", "activePane" => "bottomLeft"],
+			],
+		];
+	}
+
+	public function testColumnsWidths() {
+		$filename = tempnam("/tmp", "xlsx_writer");
+
+		$header = ['0'=>'string','1'=>'string','2'=>'string','3'=>'string'];
+		$sheet = [
+			['55','66','77','88'],
+			['10','11','12','13'],
+		];
+
+		$widths = [10, 20, 30, 40];
+
+		$col_options = ['widths' => $widths];
+
+		$xlsx_writer = new XLSXWriter();
+		$xlsx_writer->writeSheetHeader('mysheet', $header, $format = 'xlsx', $delimiter = ';', $subheader = NULL, $col_options);
+		$xlsx_writer->writeSheetRow('mysheet', $sheet[0]);
+		$xlsx_writer->writeSheetRow('mysheet', $sheet[1]);
+		$xlsx_writer->writeToFile($filename);
+
+		$zip = new ZipArchive();
+		$r = $zip->open($filename);
+		$xml = $this->extractSheetXml($zip);
+
+		$this->assertTrue($r);
+		$this->assertNotEmpty(($zip->numFiles));
+		$this->assertNotEmpty($xml);
+
+		$cols = $xml->cols->col;
+		foreach ($widths as $col_index => $col_width) {
+			$col = $cols[$col_index];
+			$this->assertFalse(filter_var($col["collapsed"], FILTER_VALIDATE_BOOLEAN));
+			$this->assertFalse(filter_var($col["hidden"], FILTER_VALIDATE_BOOLEAN));
+			$this->assertTrue(filter_var($col["customWidth"], FILTER_VALIDATE_BOOLEAN));
+			$this->assertEquals($col_index + 1, (string) $col["max"]);
+			$this->assertEquals($col_index + 1, (string) $col["min"]);
+			$this->assertEquals("0", (string) $col["style"]);
+			$this->assertEquals($col_width, (string) $col["width"]);
+		}
+		$last_col_index = count($widths);
+		$last_col = $cols[$last_col_index];
+		$this->assertFalse(filter_var($last_col["collapsed"], FILTER_VALIDATE_BOOLEAN));
+		$this->assertFalse(filter_var($last_col["hidden"], FILTER_VALIDATE_BOOLEAN));
+		$this->assertFalse(filter_var($last_col["customWidth"], FILTER_VALIDATE_BOOLEAN));
+		$this->assertEquals("1024", (string) $last_col["max"]);
+		$this->assertEquals($last_col_index + 1, (string) $last_col["min"]);
+		$this->assertEquals("0", (string) $last_col["style"]);
+		$this->assertEquals("11.5", (string) $last_col["width"]);
+
+		$zip->close();
+		@unlink($filename);
+	}
+
+	public function testRowHeight() {
+		$filename = tempnam("/tmp", "xlsx_writer");
+
+		$sheet = [
+			['55','66','77','88'],
+			['10','11','12','13'],
+		];
+
+		$custom_height = 20.5;
+
+		$row_options = ['height' => $custom_height];
+
+		$xlsx_writer = new XLSXWriter();
+		$xlsx_writer->writeSheetRow('mysheet', $sheet[0], $format = 'xlsx', $delimiter = ';', $row_options);
+		$xlsx_writer->writeSheetRow('mysheet', $sheet[1]);
+		$xlsx_writer->writeToFile($filename);
+
+		$zip = new ZipArchive();
+		$r = $zip->open($filename);
+		$xml = $this->extractSheetXml($zip);
+
+		$this->assertTrue($r);
+		$this->assertNotEmpty(($zip->numFiles));
+		$this->assertNotEmpty($xml);
+
+		$rows = $xml->sheetData->row;
+		$this->assertRowProperties($custom_height, $expected_custom_height = true, $expected_hidden = false, $expected_collapsed = false, $rows[0]);
+		$this->assertRowProperties($expected_height = 12.1, $expected_custom_height = false, $expected_hidden = false, $expected_collapsed = false, $rows[1]);
+
+		$zip->close();
+		@unlink($filename);
+	}
+
+	public function testRowHidden() {
+		$filename = tempnam("/tmp", "xlsx_writer");
+
+		$sheet = [
+			['55','66','77','88'],
+			['10','11','12','13'],
+		];
+
+		$row_options = ['hidden' => true];
+
+		$expected_height = 12.1;
+		$expected_custom_height = false;
+		$expected_collapsed = false;
+
+		$xlsx_writer = new XLSXWriter();
+		$xlsx_writer->writeSheetRow('mysheet', $sheet[0], $format = 'xlsx', $delimiter = ';', $row_options);
+		$xlsx_writer->writeSheetRow('mysheet', $sheet[1]);
+		$xlsx_writer->writeToFile($filename);
+
+		$zip = new ZipArchive();
+		$r = $zip->open($filename);
+		$xml = $this->extractSheetXml($zip);
+
+		$this->assertTrue($r);
+		$this->assertNotEmpty(($zip->numFiles));
+		$this->assertNotEmpty($xml);
+
+		$rows = $xml->sheetData->row;
+		$this->assertRowProperties($expected_height, $expected_custom_height, $expected_hidden = true, $expected_collapsed, $rows[0]);
+		$this->assertRowProperties($expected_height, $expected_custom_height, $expected_hidden = false, $expected_collapsed, $rows[1]);
+
+		$zip->close();
+		@unlink($filename);
+	}
+
+	public function testRowCollapsed() {
+		$filename = tempnam("/tmp", "xlsx_writer");
+
+		$sheet = [
+			['55','66','77','88'],
+			['10','11','12','13'],
+		];
+
+		$row_options = ['collapsed' => true];
+
+		$expected_height = 12.1;
+		$expected_custom_height = false;
+		$expected_hidden = false;
+
+		$xlsx_writer = new XLSXWriter();
+		$xlsx_writer->writeSheetRow('mysheet', $sheet[0], $format = 'xlsx', $delimiter = ';', $row_options);
+		$xlsx_writer->writeSheetRow('mysheet', $sheet[1]);
+		$xlsx_writer->writeToFile($filename);
+
+		$zip = new ZipArchive();
+		$r = $zip->open($filename);
+		$xml = $this->extractSheetXml($zip);
+
+		$this->assertTrue($r);
+		$this->assertNotEmpty(($zip->numFiles));
+		$this->assertNotEmpty($xml);
+
+		$rows = $xml->sheetData->row;
+		$this->assertRowProperties($expected_height, $expected_custom_height, $expected_hidden, $expected_collapsed = true, $rows[0]);
+		$this->assertRowProperties($expected_height, $expected_custom_height, $expected_hidden, $expected_collapsed = false, $rows[1]);
+
+		$zip->close();
+		@unlink($filename);
+	}
+
+	public function testAddBorder() {
+		$filename = tempnam("/tmp", "xlsx_writer");
+
+		$header = ["0"=>"string", "1"=>"string", "2"=>"string", "3"=>"string"];
+		$sheet = [
+			["55", "66", "77", "88"],
+			["10", "11", "12", "13"],
+		];
+
+		$expected_borders = ["right", "left", "top", "bottom"];
+		$expected_border_style = "thick";
+		$expected_border_color_base = "ff99cc";
+		$expected_border_color = "FFFF99CC";
+
+		$row_options = [
+			"border" => implode(",", $expected_borders),
+			"border-style" => $expected_border_style,
+			"border-color" => "#$expected_border_color_base" ,
+		];
+
+		$xlsx_writer = new XLSXWriter();
+		$xlsx_writer->writeSheetHeader("mysheet", $header);
+		$xlsx_writer->writeSheetRow("mysheet", $sheet[0], $format = "xlsx", $delimiter = ";", $row_options);
+		$xlsx_writer->writeSheetRow("mysheet", $sheet[1]);
+		$xlsx_writer->writeToFile($filename);
+
+		$zip = new ZipArchive();
+		$r = $zip->open($filename);
+		$xml = $this->extractSheetXml($zip);
+		$styles = $this->extractStyleXml($zip);
+
+		$this->assertTrue($r);
+		$this->assertNotEmpty(($zip->numFiles));
+		$this->assertNotEmpty($xml);
+		$this->assertNotEmpty($styles);
+
+		$border_styles = $styles->borders;
+		$this->assertBorderStyle($expected_border_style, $expected_border_color, $border = $border_styles->border[1]);
+		$this->assertFillStyle($expected_pattern = "solid", $expected_bg_color = "FF003300", $styles->fills->fill[2]);
+		$this->assertFontStyle($expected_font_name = "Arial", $expected_is_bold = "true", $styles->fonts->font[4]);
+
+		$cell_styles = $styles->cellXfs->xf;
+		$this->assertCellStyle($expected_apply_border_string = "false", $expected_border_id = 0, $expected_fill_id = 2, $expected_font_id = 4, $cell_styles[6]);
+		$this->assertCellStyle($expected_apply_border_string = "true", $expected_border_id = 1, $expected_fill_id = 0, $expected_font_id = 0, $cell_styles[7]);
+
+		$rows = $xml->sheetData->row;
+		$this->assertRowHasStyleIndex($rows[0], $expected_header_style = 6);
+		$this->assertRowHasStyleIndex($rows[1], $expected_style = 7);
+
+		$zip->close();
+		@unlink($filename);
+	}
+
+	private function stripCellsFromSheetXML($sheet_xml) {
+		$output = [];
+
+		$xml = new SimpleXMLElement($sheet_xml);
+
+		for ($i = 0; $i < count($xml->sheetData->row); $i++) {
+			$row = $xml->sheetData->row[$i];
+			for ($j = 0; $j < count($row->c); $j ++) {
+				$output[$i][$j] = (string)$row->c[$j]->v;
+			}
+		}
+
+		return $output;
+	}
+
+	public static function array_diff_assoc_recursive($array1, $array2) {
+		$difference = [];
+		foreach($array1 as $key => $value) {
+			if(is_array($value)) {
+				if(!isset($array2[$key]) || !is_array($array2[$key])) {
+					$difference[$key] = $value;
+				} else {
+					$new_diff = self::array_diff_assoc_recursive($value, $array2[$key]);
+					if(!empty($new_diff)) {
+						$difference[$key] = $new_diff;
+					}
+				}
+			} else if(!isset($array2[$key]) || $array2[$key] != $value) {
+				$difference[$key] = $value;
+			}
+		}
+
+		return empty($difference) ? [] : $difference;
+	}
+
+	private function extractSheetXml($zip) {
+		for($z=0; $z < $zip->numFiles; $z++) {
+			$inside_zip_filename = $zip->getNameIndex($z);
+			$sheet_xml = $zip->getFromName($inside_zip_filename);
+			if (preg_match("/sheet(\d+).xml/", basename($inside_zip_filename))) {
+				return new SimpleXMLElement($sheet_xml);
+			}
+		}
+
+		return null;
+	}
+
+	private function extractStyleXml($zip) {
+		for($z=0; $z < $zip->numFiles; $z++) {
+			$inside_zip_filename = $zip->getNameIndex($z);
+			$xml = $zip->getFromName($inside_zip_filename);
+			if (preg_match("/styles.xml/", basename($inside_zip_filename))) {
+				return new SimpleXMLElement($xml);
+			}
+		}
+
+		return null;
+	}
+
+	private function assertRowProperties($expected_height, $expected_custom_height, $expected_hidden, $expected_collapsed, $row) {
+		$this->assertEquals($expected_height, (string)$row['ht']);
+		$this->assertEquals($expected_custom_height, filter_var($row['customHeight'], FILTER_VALIDATE_BOOLEAN));
+		$this->assertEquals($expected_hidden, filter_var($row['hidden'], FILTER_VALIDATE_BOOLEAN));
+		$this->assertEquals($expected_collapsed, filter_var($row['collapsed'], FILTER_VALIDATE_BOOLEAN));
+	}
+
+	private function assertCellStyle($expected_apply_border_string, $expected_border_id, $expected_fill_id, $expected_font_id, $cell_style) {
+		$this->assertEquals($expected_apply_border_string, $cell_style["applyBorder"]);
+		$this->assertEquals($expected_border_id, (int)$cell_style["borderId"]);
+		$this->assertEquals($expected_fill_id, (int)$cell_style["fillId"]);
+		$this->assertEquals($expected_font_id, (int)$cell_style["fontId"]);
+	}
+
+	private function assertBorderStyle($expected_border_style, $expected_border_color, $border) {
+		$this->assertEquals($expected_border_style, $border->left["style"]);
+		$this->assertEquals($expected_border_style, $border->right["style"]);
+		$this->assertEquals($expected_border_style, $border->top["style"]);
+		$this->assertEquals($expected_border_style, $border->bottom["style"]);
+
+		$this->assertEquals($expected_border_color, $border->left->color["rgb"]);
+		$this->assertEquals($expected_border_color, $border->right->color["rgb"]);
+		$this->assertEquals($expected_border_color, $border->top->color["rgb"]);
+		$this->assertEquals($expected_border_color, $border->bottom->color["rgb"]);
+	}
+
+	private function assertFillStyle($expected_pattern, $expected_bg_color, $fill) {
+		$this->assertEquals($expected_pattern, $fill->patternFill["patternType"]);
+		if (!empty($expected_bg_color)) {
+			$this->assertEquals($expected_bg_color, $fill->patternFill->bgColor["rgb"]);
+		}
+	}
+
+	private function assertFontStyle($expected_font_name, $expected_is_bold, $font) {
+ 		$this->assertEquals($expected_font_name, $font->name["val"]);
+ 		if (!empty($expected_is_bold)) {
+		    $this->assertEquals($expected_is_bold, $font->b["val"]);
+	    }
+	}
+
+	private function assertRowHasStyleIndex($row, $expected_style) {
+		foreach ($row->c as $cell) {
+			$this->assertEquals($expected_style, (int)$cell["s"]);
+		}
+	}
+}
diff --git a/composer.json b/composer.json
index c963afe2e217ffc9cfacd04401f7933b03f67319..b6a6b4512bca70bc59806ba89074f9b121b163a1 100644
--- a/composer.json
+++ b/composer.json
@@ -10,6 +10,6 @@
         "classmap": ["xlsxwriter.class.php"]
     },
     "require-dev": {
-        "phpunit/phpunit": "4.3.*"
+        "phpunit/phpunit": "^7.5"
     }
 }
diff --git a/composer.lock b/composer.lock
index 532dd85d9aef7e984d75f0af8fa2c12512a593c3..87737532d49722e0501e386e3d12e9a5e78a265f 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,45 +1,47 @@
 {
     "_readme": [
         "This file locks the dependencies of your project to a known state",
-        "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "hash": "89c1f54283c6b465c6b1a751a5270d78",
+    "content-hash": "9c5833be9ba3f93c4ddfb4ac7316b78a",
     "packages": [],
     "packages-dev": [
         {
             "name": "doctrine/instantiator",
-            "version": "1.0.4",
+            "version": "1.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/instantiator.git",
-                "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119"
+                "reference": "a2c590166b2133a4633738648b6b064edae0814a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f976e5de371104877ebc89bd8fecb0019ed9c119",
-                "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119",
+                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a",
+                "reference": "a2c590166b2133a4633738648b6b064edae0814a",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3,<8.0-DEV"
+                "php": "^7.1"
             },
             "require-dev": {
-                "athletic/athletic": "~0.1.8",
+                "doctrine/coding-standard": "^6.0",
                 "ext-pdo": "*",
                 "ext-phar": "*",
-                "phpunit/phpunit": "~4.0",
-                "squizlabs/php_codesniffer": "2.0.*@ALPHA"
+                "phpbench/phpbench": "^0.13",
+                "phpstan/phpstan-phpunit": "^0.11",
+                "phpstan/phpstan-shim": "^0.11",
+                "phpunit/phpunit": "^7.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "1.2.x-dev"
                 }
             },
             "autoload": {
-                "psr-0": {
-                    "Doctrine\\Instantiator\\": "src"
+                "psr-4": {
+                    "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -54,48 +56,414 @@
                 }
             ],
             "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
-            "homepage": "https://github.com/doctrine/instantiator",
+            "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
             "keywords": [
                 "constructor",
                 "instantiate"
             ],
-            "time": "2014-10-13 12:58:55"
+            "time": "2019-03-17T17:37:11+00:00"
+        },
+        {
+            "name": "myclabs/deep-copy",
+            "version": "1.9.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/myclabs/DeepCopy.git",
+                "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72",
+                "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1"
+            },
+            "replace": {
+                "myclabs/deep-copy": "self.version"
+            },
+            "require-dev": {
+                "doctrine/collections": "^1.0",
+                "doctrine/common": "^2.6",
+                "phpunit/phpunit": "^7.1"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "DeepCopy\\": "src/DeepCopy/"
+                },
+                "files": [
+                    "src/DeepCopy/deep_copy.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Create deep copies (clones) of your objects",
+            "keywords": [
+                "clone",
+                "copy",
+                "duplicate",
+                "object",
+                "object graph"
+            ],
+            "time": "2019-04-07T13:18:21+00:00"
+        },
+        {
+            "name": "phar-io/manifest",
+            "version": "1.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phar-io/manifest.git",
+                "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
+                "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-phar": "*",
+                "phar-io/version": "^2.0",
+                "php": "^5.6 || ^7.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Heuer",
+                    "email": "sebastian@phpeople.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+            "time": "2018-07-08T19:23:20+00:00"
+        },
+        {
+            "name": "phar-io/version",
+            "version": "2.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phar-io/version.git",
+                "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6",
+                "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.6 || ^7.0"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Heuer",
+                    "email": "sebastian@phpeople.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Library for handling version information and constraints",
+            "time": "2018-07-08T19:19:57+00:00"
+        },
+        {
+            "name": "phpdocumentor/reflection-common",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+                "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
+                "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.6"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": [
+                        "src"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jaap van Otterdijk",
+                    "email": "opensource@ijaap.nl"
+                }
+            ],
+            "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
+            "homepage": "http://www.phpdoc.org",
+            "keywords": [
+                "FQSEN",
+                "phpDocumentor",
+                "phpdoc",
+                "reflection",
+                "static analysis"
+            ],
+            "time": "2017-09-11T18:02:19+00:00"
+        },
+        {
+            "name": "phpdocumentor/reflection-docblock",
+            "version": "4.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+                "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c",
+                "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0",
+                "phpdocumentor/reflection-common": "^1.0.0",
+                "phpdocumentor/type-resolver": "^0.4.0",
+                "webmozart/assert": "^1.0"
+            },
+            "require-dev": {
+                "doctrine/instantiator": "~1.0.5",
+                "mockery/mockery": "^1.0",
+                "phpunit/phpunit": "^6.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": [
+                        "src/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "me@mikevanriel.com"
+                }
+            ],
+            "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+            "time": "2019-04-30T17:48:53+00:00"
+        },
+        {
+            "name": "phpdocumentor/type-resolver",
+            "version": "0.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/TypeResolver.git",
+                "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
+                "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5 || ^7.0",
+                "phpdocumentor/reflection-common": "^1.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "^0.9.4",
+                "phpunit/phpunit": "^5.2||^4.8.24"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": [
+                        "src/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "me@mikevanriel.com"
+                }
+            ],
+            "time": "2017-07-14T14:27:02+00:00"
+        },
+        {
+            "name": "phpspec/prophecy",
+            "version": "1.8.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpspec/prophecy.git",
+                "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
+                "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.0.2",
+                "php": "^5.3|^7.0",
+                "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
+                "sebastian/comparator": "^1.1|^2.0|^3.0",
+                "sebastian/recursion-context": "^1.0|^2.0|^3.0"
+            },
+            "require-dev": {
+                "phpspec/phpspec": "^2.5|^3.2",
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.8.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Prophecy\\": "src/Prophecy"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Konstantin Kudryashov",
+                    "email": "ever.zet@gmail.com",
+                    "homepage": "http://everzet.com"
+                },
+                {
+                    "name": "Marcello Duarte",
+                    "email": "marcello.duarte@gmail.com"
+                }
+            ],
+            "description": "Highly opinionated mocking framework for PHP 5.3+",
+            "homepage": "https://github.com/phpspec/prophecy",
+            "keywords": [
+                "Double",
+                "Dummy",
+                "fake",
+                "mock",
+                "spy",
+                "stub"
+            ],
+            "time": "2019-06-13T12:50:23+00:00"
         },
         {
             "name": "phpunit/php-code-coverage",
-            "version": "2.0.11",
+            "version": "6.1.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "53603b3c995f5aab6b59c8e08c3a663d2cc810b7"
+                "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/53603b3c995f5aab6b59c8e08c3a663d2cc810b7",
-                "reference": "53603b3c995f5aab6b59c8e08c3a663d2cc810b7",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d",
+                "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3",
-                "phpunit/php-file-iterator": "~1.3",
-                "phpunit/php-text-template": "~1.2",
-                "phpunit/php-token-stream": "~1.3",
-                "sebastian/environment": "~1.0",
-                "sebastian/version": "~1.0"
+                "ext-dom": "*",
+                "ext-xmlwriter": "*",
+                "php": "^7.1",
+                "phpunit/php-file-iterator": "^2.0",
+                "phpunit/php-text-template": "^1.2.1",
+                "phpunit/php-token-stream": "^3.0",
+                "sebastian/code-unit-reverse-lookup": "^1.0.1",
+                "sebastian/environment": "^3.1 || ^4.0",
+                "sebastian/version": "^2.0.1",
+                "theseer/tokenizer": "^1.1"
             },
             "require-dev": {
-                "ext-xdebug": ">=2.1.4",
-                "phpunit/phpunit": "~4.1"
+                "phpunit/phpunit": "^7.0"
             },
             "suggest": {
-                "ext-dom": "*",
-                "ext-xdebug": ">=2.2.1",
-                "ext-xmlwriter": "*"
+                "ext-xdebug": "^2.6.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.0.x-dev"
+                    "dev-master": "6.1-dev"
                 }
             },
             "autoload": {
@@ -104,186 +472,610 @@
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
-            "include-path": [
-                ""
-            ],
             "license": [
                 "BSD-3-Clause"
             ],
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sb@sebastian-bergmann.de",
-                    "role": "lead"
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+            "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+            "keywords": [
+                "coverage",
+                "testing",
+                "xunit"
+            ],
+            "time": "2018-10-31T16:06:48+00:00"
+        },
+        {
+            "name": "phpunit/php-file-iterator",
+            "version": "2.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+                "reference": "050bedf145a257b1ff02746c31894800e5122946"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946",
+                "reference": "050bedf145a257b1ff02746c31894800e5122946",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+            "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+            "keywords": [
+                "filesystem",
+                "iterator"
+            ],
+            "time": "2018-09-13T20:33:42+00:00"
+        },
+        {
+            "name": "phpunit/php-text-template",
+            "version": "1.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-text-template.git",
+                "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+                "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Simple template engine.",
+            "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+            "keywords": [
+                "template"
+            ],
+            "time": "2015-06-21T13:50:34+00:00"
+        },
+        {
+            "name": "phpunit/php-timer",
+            "version": "2.1.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-timer.git",
+                "reference": "1038454804406b0b5f5f520358e78c1c2f71501e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e",
+                "reference": "1038454804406b0b5f5f520358e78c1c2f71501e",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Utility class for timing",
+            "homepage": "https://github.com/sebastianbergmann/php-timer/",
+            "keywords": [
+                "timer"
+            ],
+            "time": "2019-06-07T04:22:29+00:00"
+        },
+        {
+            "name": "phpunit/php-token-stream",
+            "version": "3.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-token-stream.git",
+                "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18",
+                "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18",
+                "shasum": ""
+            },
+            "require": {
+                "ext-tokenizer": "*",
+                "php": "^7.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Wrapper around PHP's tokenizer extension.",
+            "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
+            "keywords": [
+                "tokenizer"
+            ],
+            "time": "2018-10-30T05:52:18+00:00"
+        },
+        {
+            "name": "phpunit/phpunit",
+            "version": "7.5.13",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/phpunit.git",
+                "reference": "b9278591caa8630127f96c63b598712b699e671c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b9278591caa8630127f96c63b598712b699e671c",
+                "reference": "b9278591caa8630127f96c63b598712b699e671c",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.1",
+                "ext-dom": "*",
+                "ext-json": "*",
+                "ext-libxml": "*",
+                "ext-mbstring": "*",
+                "ext-xml": "*",
+                "myclabs/deep-copy": "^1.7",
+                "phar-io/manifest": "^1.0.2",
+                "phar-io/version": "^2.0",
+                "php": "^7.1",
+                "phpspec/prophecy": "^1.7",
+                "phpunit/php-code-coverage": "^6.0.7",
+                "phpunit/php-file-iterator": "^2.0.1",
+                "phpunit/php-text-template": "^1.2.1",
+                "phpunit/php-timer": "^2.1",
+                "sebastian/comparator": "^3.0",
+                "sebastian/diff": "^3.0",
+                "sebastian/environment": "^4.0",
+                "sebastian/exporter": "^3.1",
+                "sebastian/global-state": "^2.0",
+                "sebastian/object-enumerator": "^3.0.3",
+                "sebastian/resource-operations": "^2.0",
+                "sebastian/version": "^2.0.1"
+            },
+            "conflict": {
+                "phpunit/phpunit-mock-objects": "*"
+            },
+            "require-dev": {
+                "ext-pdo": "*"
+            },
+            "suggest": {
+                "ext-soap": "*",
+                "ext-xdebug": "*",
+                "phpunit/php-invoker": "^2.0"
+            },
+            "bin": [
+                "phpunit"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "7.5-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "The PHP Unit Testing framework.",
+            "homepage": "https://phpunit.de/",
+            "keywords": [
+                "phpunit",
+                "testing",
+                "xunit"
+            ],
+            "time": "2019-06-19T12:01:51+00:00"
+        },
+        {
+            "name": "sebastian/code-unit-reverse-lookup",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+                "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
+                "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.6 || ^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^5.7 || ^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Looks up which function or method a line of code belongs to",
+            "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+            "time": "2017-03-04T06:30:41+00:00"
+        },
+        {
+            "name": "sebastian/comparator",
+            "version": "3.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/comparator.git",
+                "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
+                "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1",
+                "sebastian/diff": "^3.0",
+                "sebastian/exporter": "^3.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@2bepublished.at"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
-            "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
-            "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+            "description": "Provides the functionality to compare PHP values for equality",
+            "homepage": "https://github.com/sebastianbergmann/comparator",
             "keywords": [
-                "coverage",
-                "testing",
-                "xunit"
+                "comparator",
+                "compare",
+                "equality"
             ],
-            "time": "2014-08-31 06:33:04"
+            "time": "2018-07-12T15:12:46+00:00"
         },
         {
-            "name": "phpunit/php-file-iterator",
-            "version": "1.3.4",
+            "name": "sebastian/diff",
+            "version": "3.0.2",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
-                "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb"
+                "url": "https://github.com/sebastianbergmann/diff.git",
+                "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb",
-                "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb",
+                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
+                "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3"
+                "php": "^7.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7.5 || ^8.0",
+                "symfony/process": "^2 || ^3.3 || ^4"
             },
             "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
             "autoload": {
                 "classmap": [
-                    "File/"
+                    "src/"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
-            "include-path": [
-                ""
-            ],
             "license": [
                 "BSD-3-Clause"
             ],
             "authors": [
+                {
+                    "name": "Kore Nordmann",
+                    "email": "mail@kore-nordmann.de"
+                },
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sb@sebastian-bergmann.de",
-                    "role": "lead"
+                    "email": "sebastian@phpunit.de"
                 }
             ],
-            "description": "FilterIterator implementation that filters files based on a list of suffixes.",
-            "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+            "description": "Diff implementation",
+            "homepage": "https://github.com/sebastianbergmann/diff",
             "keywords": [
-                "filesystem",
-                "iterator"
+                "diff",
+                "udiff",
+                "unidiff",
+                "unified diff"
             ],
-            "time": "2013-10-10 15:34:57"
+            "time": "2019-02-04T06:01:07+00:00"
         },
         {
-            "name": "phpunit/php-text-template",
-            "version": "1.2.0",
+            "name": "sebastian/environment",
+            "version": "4.2.2",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-text-template.git",
-                "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a"
+                "url": "https://github.com/sebastianbergmann/environment.git",
+                "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
-                "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
+                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404",
+                "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3"
+                "php": "^7.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7.5"
+            },
+            "suggest": {
+                "ext-posix": "*"
             },
             "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.2-dev"
+                }
+            },
             "autoload": {
                 "classmap": [
-                    "Text/"
+                    "src/"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
-            "include-path": [
-                ""
-            ],
             "license": [
                 "BSD-3-Clause"
             ],
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sb@sebastian-bergmann.de",
-                    "role": "lead"
+                    "email": "sebastian@phpunit.de"
                 }
             ],
-            "description": "Simple template engine.",
-            "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+            "description": "Provides functionality to handle HHVM/PHP environments",
+            "homepage": "http://www.github.com/sebastianbergmann/environment",
             "keywords": [
-                "template"
+                "Xdebug",
+                "environment",
+                "hhvm"
             ],
-            "time": "2014-01-30 17:20:04"
+            "time": "2019-05-05T09:05:15+00:00"
         },
         {
-            "name": "phpunit/php-timer",
-            "version": "1.0.5",
+            "name": "sebastian/exporter",
+            "version": "3.1.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-timer.git",
-                "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c"
+                "url": "https://github.com/sebastianbergmann/exporter.git",
+                "reference": "234199f4528de6d12aaa58b612e98f7d36adb937"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
-                "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937",
+                "reference": "234199f4528de6d12aaa58b612e98f7d36adb937",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3"
+                "php": "^7.0",
+                "sebastian/recursion-context": "^3.0"
+            },
+            "require-dev": {
+                "ext-mbstring": "*",
+                "phpunit/phpunit": "^6.0"
             },
             "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.1.x-dev"
+                }
+            },
             "autoload": {
                 "classmap": [
-                    "PHP/"
+                    "src/"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
-            "include-path": [
-                ""
-            ],
             "license": [
                 "BSD-3-Clause"
             ],
             "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@2bepublished.at"
+                },
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sb@sebastian-bergmann.de",
-                    "role": "lead"
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
                 }
             ],
-            "description": "Utility class for timing",
-            "homepage": "https://github.com/sebastianbergmann/php-timer/",
+            "description": "Provides the functionality to export PHP variables for visualization",
+            "homepage": "http://www.github.com/sebastianbergmann/exporter",
             "keywords": [
-                "timer"
+                "export",
+                "exporter"
             ],
-            "time": "2013-08-02 07:42:54"
+            "time": "2017-04-03T13:19:02+00:00"
         },
         {
-            "name": "phpunit/php-token-stream",
-            "version": "1.3.0",
+            "name": "sebastian/global-state",
+            "version": "2.0.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-token-stream.git",
-                "reference": "f8d5d08c56de5cfd592b3340424a81733259a876"
+                "url": "https://github.com/sebastianbergmann/global-state.git",
+                "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/f8d5d08c56de5cfd592b3340424a81733259a876",
-                "reference": "f8d5d08c56de5cfd592b3340424a81733259a876",
+                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4",
+                "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4",
                 "shasum": ""
             },
             "require": {
-                "ext-tokenizer": "*",
-                "php": ">=5.3.3"
+                "php": "^7.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "~4.2"
+                "phpunit/phpunit": "^6.0"
+            },
+            "suggest": {
+                "ext-uopz": "*"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.3-dev"
+                    "dev-master": "2.0-dev"
                 }
             },
             "autoload": {
@@ -301,56 +1093,39 @@
                     "email": "sebastian@phpunit.de"
                 }
             ],
-            "description": "Wrapper around PHP's tokenizer extension.",
-            "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
+            "description": "Snapshotting of global state",
+            "homepage": "http://www.github.com/sebastianbergmann/global-state",
             "keywords": [
-                "tokenizer"
+                "global state"
             ],
-            "time": "2014-08-31 06:12:13"
+            "time": "2017-04-27T15:39:26+00:00"
         },
         {
-            "name": "phpunit/phpunit",
-            "version": "4.3.5",
+            "name": "sebastian/object-enumerator",
+            "version": "3.0.3",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "2dab9d593997db4abcf58d0daf798eb4e9cecfe1"
+                "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+                "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2dab9d593997db4abcf58d0daf798eb4e9cecfe1",
-                "reference": "2dab9d593997db4abcf58d0daf798eb4e9cecfe1",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5",
+                "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5",
                 "shasum": ""
             },
             "require": {
-                "ext-dom": "*",
-                "ext-json": "*",
-                "ext-pcre": "*",
-                "ext-reflection": "*",
-                "ext-spl": "*",
-                "php": ">=5.3.3",
-                "phpunit/php-code-coverage": "~2.0",
-                "phpunit/php-file-iterator": "~1.3.2",
-                "phpunit/php-text-template": "~1.2",
-                "phpunit/php-timer": "~1.0.2",
-                "phpunit/phpunit-mock-objects": "~2.3",
-                "sebastian/comparator": "~1.0",
-                "sebastian/diff": "~1.1",
-                "sebastian/environment": "~1.0",
-                "sebastian/exporter": "~1.0",
-                "sebastian/version": "~1.0",
-                "symfony/yaml": "~2.0"
+                "php": "^7.0",
+                "sebastian/object-reflector": "^1.1.1",
+                "sebastian/recursion-context": "^3.0"
             },
-            "suggest": {
-                "phpunit/php-invoker": "~1.1"
+            "require-dev": {
+                "phpunit/phpunit": "^6.0"
             },
-            "bin": [
-                "phpunit"
-            ],
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "4.3.x-dev"
+                    "dev-master": "3.0.x-dev"
                 }
             },
             "autoload": {
@@ -359,58 +1134,43 @@
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
-            "include-path": [
-                "",
-                "../../symfony/yaml/"
-            ],
             "license": [
                 "BSD-3-Clause"
             ],
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "email": "sebastian@phpunit.de"
                 }
             ],
-            "description": "The PHP Unit Testing framework.",
-            "homepage": "http://www.phpunit.de/",
-            "keywords": [
-                "phpunit",
-                "testing",
-                "xunit"
-            ],
-            "time": "2014-11-11 10:11:09"
+            "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+            "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+            "time": "2017-08-03T12:35:26+00:00"
         },
         {
-            "name": "phpunit/phpunit-mock-objects",
-            "version": "2.3.0",
+            "name": "sebastian/object-reflector",
+            "version": "1.1.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
-                "reference": "c63d2367247365f688544f0d500af90a11a44c65"
+                "url": "https://github.com/sebastianbergmann/object-reflector.git",
+                "reference": "773f97c67f28de00d397be301821b06708fca0be"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/c63d2367247365f688544f0d500af90a11a44c65",
-                "reference": "c63d2367247365f688544f0d500af90a11a44c65",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be",
+                "reference": "773f97c67f28de00d397be301821b06708fca0be",
                 "shasum": ""
             },
             "require": {
-                "doctrine/instantiator": "~1.0,>=1.0.1",
-                "php": ">=5.3.3",
-                "phpunit/php-text-template": "~1.2"
+                "php": "^7.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "~4.3"
-            },
-            "suggest": {
-                "ext-soap": "*"
+                "phpunit/phpunit": "^6.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.3.x-dev"
+                    "dev-master": "1.1-dev"
                 }
             },
             "autoload": {
@@ -425,44 +1185,37 @@
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sb@sebastian-bergmann.de",
-                    "role": "lead"
+                    "email": "sebastian@phpunit.de"
                 }
             ],
-            "description": "Mock Object library for PHPUnit",
-            "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
-            "keywords": [
-                "mock",
-                "xunit"
-            ],
-            "time": "2014-10-03 05:12:11"
+            "description": "Allows reflection of object attributes, including inherited and non-public ones",
+            "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+            "time": "2017-03-29T09:07:27+00:00"
         },
         {
-            "name": "sebastian/comparator",
-            "version": "1.0.1",
+            "name": "sebastian/recursion-context",
+            "version": "3.0.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/comparator.git",
-                "reference": "e54a01c0da1b87db3c5a3c4c5277ddf331da4aef"
+                "url": "https://github.com/sebastianbergmann/recursion-context.git",
+                "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e54a01c0da1b87db3c5a3c4c5277ddf331da4aef",
-                "reference": "e54a01c0da1b87db3c5a3c4c5277ddf331da4aef",
+                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
+                "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3",
-                "sebastian/diff": "~1.1",
-                "sebastian/exporter": "~1.0"
+                "php": "^7.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "~4.1"
+                "phpunit/phpunit": "^6.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "3.0.x-dev"
                 }
             },
             "autoload": {
@@ -479,52 +1232,40 @@
                     "name": "Jeff Welch",
                     "email": "whatthejeff@gmail.com"
                 },
-                {
-                    "name": "Volker Dusch",
-                    "email": "github@wallbash.com"
-                },
-                {
-                    "name": "Bernhard Schussek",
-                    "email": "bschussek@2bepublished.at"
-                },
                 {
                     "name": "Sebastian Bergmann",
                     "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
                 }
             ],
-            "description": "Provides the functionality to compare PHP values for equality",
-            "homepage": "http://www.github.com/sebastianbergmann/comparator",
-            "keywords": [
-                "comparator",
-                "compare",
-                "equality"
-            ],
-            "time": "2014-05-11 23:00:21"
+            "description": "Provides functionality to recursively process PHP variables",
+            "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+            "time": "2017-03-03T06:23:57+00:00"
         },
         {
-            "name": "sebastian/diff",
-            "version": "1.2.0",
+            "name": "sebastian/resource-operations",
+            "version": "2.0.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/diff.git",
-                "reference": "5843509fed39dee4b356a306401e9dd1a931fec7"
+                "url": "https://github.com/sebastianbergmann/resource-operations.git",
+                "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/5843509fed39dee4b356a306401e9dd1a931fec7",
-                "reference": "5843509fed39dee4b356a306401e9dd1a931fec7",
+                "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
+                "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "~4.2"
+                "php": "^7.1"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.2-dev"
+                    "dev-master": "2.0-dev"
                 }
             },
             "autoload": {
@@ -537,46 +1278,36 @@
                 "BSD-3-Clause"
             ],
             "authors": [
-                {
-                    "name": "Kore Nordmann",
-                    "email": "mail@kore-nordmann.de"
-                },
                 {
                     "name": "Sebastian Bergmann",
                     "email": "sebastian@phpunit.de"
                 }
             ],
-            "description": "Diff implementation",
-            "homepage": "http://www.github.com/sebastianbergmann/diff",
-            "keywords": [
-                "diff"
-            ],
-            "time": "2014-08-15 10:29:00"
+            "description": "Provides a list of PHP built-in functions that operate on resources",
+            "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+            "time": "2018-10-04T04:07:39+00:00"
         },
         {
-            "name": "sebastian/environment",
-            "version": "1.2.0",
+            "name": "sebastian/version",
+            "version": "2.0.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/environment.git",
-                "reference": "0d9bf79554d2a999da194a60416c15cf461eb67d"
+                "url": "https://github.com/sebastianbergmann/version.git",
+                "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/0d9bf79554d2a999da194a60416c15cf461eb67d",
-                "reference": "0d9bf79554d2a999da194a60416c15cf461eb67d",
+                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
+                "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "~4.3"
+                "php": ">=5.6"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.2.x-dev"
+                    "dev-master": "2.0.x-dev"
                 }
             },
             "autoload": {
@@ -591,97 +1322,92 @@
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
                 }
             ],
-            "description": "Provides functionality to handle HHVM/PHP environments",
-            "homepage": "http://www.github.com/sebastianbergmann/environment",
-            "keywords": [
-                "Xdebug",
-                "environment",
-                "hhvm"
-            ],
-            "time": "2014-10-22 06:38:05"
+            "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+            "homepage": "https://github.com/sebastianbergmann/version",
+            "time": "2016-10-03T07:35:21+00:00"
         },
         {
-            "name": "sebastian/exporter",
-            "version": "1.0.2",
+            "name": "symfony/polyfill-ctype",
+            "version": "v1.11.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/exporter.git",
-                "reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0"
+                "url": "https://github.com/symfony/polyfill-ctype.git",
+                "reference": "82ebae02209c21113908c229e9883c419720738a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c7d59948d6e82818e1bdff7cadb6c34710eb7dc0",
-                "reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a",
+                "reference": "82ebae02209c21113908c229e9883c419720738a",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3"
             },
-            "require-dev": {
-                "phpunit/phpunit": "~4.0"
+            "suggest": {
+                "ext-ctype": "For best performance"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "1.11-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
+                "psr-4": {
+                    "Symfony\\Polyfill\\Ctype\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
+                "MIT"
             ],
             "authors": [
                 {
-                    "name": "Jeff Welch",
-                    "email": "whatthejeff@gmail.com"
-                },
-                {
-                    "name": "Volker Dusch",
-                    "email": "github@wallbash.com"
-                },
-                {
-                    "name": "Bernhard Schussek",
-                    "email": "bschussek@2bepublished.at"
-                },
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
                 },
                 {
-                    "name": "Adam Harvey",
-                    "email": "aharvey@php.net"
+                    "name": "Gert de Pagter",
+                    "email": "BackEndTea@gmail.com"
                 }
             ],
-            "description": "Provides the functionality to export PHP variables for visualization",
-            "homepage": "http://www.github.com/sebastianbergmann/exporter",
+            "description": "Symfony polyfill for ctype functions",
+            "homepage": "https://symfony.com",
             "keywords": [
-                "export",
-                "exporter"
+                "compatibility",
+                "ctype",
+                "polyfill",
+                "portable"
             ],
-            "time": "2014-09-10 00:51:36"
+            "time": "2019-02-06T07:57:58+00:00"
         },
         {
-            "name": "sebastian/version",
-            "version": "1.0.3",
+            "name": "theseer/tokenizer",
+            "version": "1.1.3",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/version.git",
-                "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43"
+                "url": "https://github.com/theseer/tokenizer.git",
+                "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43",
-                "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43",
+                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
+                "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
                 "shasum": ""
             },
+            "require": {
+                "ext-dom": "*",
+                "ext-tokenizer": "*",
+                "ext-xmlwriter": "*",
+                "php": "^7.0"
+            },
             "type": "library",
             "autoload": {
                 "classmap": [
@@ -694,42 +1420,45 @@
             ],
             "authors": [
                 {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
                 }
             ],
-            "description": "Library that helps with managing the version number of Git-hosted PHP projects",
-            "homepage": "https://github.com/sebastianbergmann/version",
-            "time": "2014-03-07 15:35:33"
+            "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+            "time": "2019-06-13T22:48:21+00:00"
         },
         {
-            "name": "symfony/yaml",
-            "version": "v2.5.6",
-            "target-dir": "Symfony/Component/Yaml",
+            "name": "webmozart/assert",
+            "version": "1.4.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/symfony/Yaml.git",
-                "reference": "2d9f527449cabfa8543dd7fa3a466d6ae83d6726"
+                "url": "https://github.com/webmozart/assert.git",
+                "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/Yaml/zipball/2d9f527449cabfa8543dd7fa3a466d6ae83d6726",
-                "reference": "2d9f527449cabfa8543dd7fa3a466d6ae83d6726",
+                "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9",
+                "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3"
+                "php": "^5.3.3 || ^7.0",
+                "symfony/polyfill-ctype": "^1.8"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.6",
+                "sebastian/version": "^1.0.1"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.5-dev"
+                    "dev-master": "1.3-dev"
                 }
             },
             "autoload": {
-                "psr-0": {
-                    "Symfony\\Component\\Yaml\\": ""
+                "psr-4": {
+                    "Webmozart\\Assert\\": "src/"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -738,23 +1467,24 @@
             ],
             "authors": [
                 {
-                    "name": "Symfony Community",
-                    "homepage": "http://symfony.com/contributors"
-                },
-                {
-                    "name": "Fabien Potencier",
-                    "email": "fabien@symfony.com"
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
                 }
             ],
-            "description": "Symfony Yaml Component",
-            "homepage": "http://symfony.com",
-            "time": "2014-10-01 05:50:18"
+            "description": "Assertions to validate method input/output with nice error messages.",
+            "keywords": [
+                "assert",
+                "check",
+                "validate"
+            ],
+            "time": "2018-12-25T11:19:39+00:00"
         }
     ],
     "aliases": [],
     "minimum-stability": "stable",
     "stability-flags": [],
     "prefer-stable": false,
+    "prefer-lowest": false,
     "platform": [],
     "platform-dev": []
 }
diff --git a/testbench/xlsxwriter.class.Test.php b/testbench/xlsxwriter.class.Test.php
index 6660e6b87c6fcea411d6ea3fd4c09a08ef23c456..c6ea5efc46bb9d32f8421522ef71175b220621ea 100644
--- a/testbench/xlsxwriter.class.Test.php
+++ b/testbench/xlsxwriter.class.Test.php
@@ -2,6 +2,8 @@
 
 include_once __DIR__.'/../vendor/autoload.php';
 
+use PHPUnit\Framework\TestCase;
+
 //TODO test double:writeSheetHeader
 //TODO test invalid UTF8
 //TODO test outoforder writeSheetRow('Sheet1',());
@@ -9,12 +11,12 @@ include_once __DIR__.'/../vendor/autoload.php';
 class _XLSXWriter_ extends XLSXWriter
 {
     public function writeCell(XLSXWriter_BuffererWriter &$file, $row_number, $column_number, $value, $cell_format) {
-		return call_user_func_array('parent::writeCell', func_get_args());
+		return call_user_func_array('parent::writeCell', [&$file, $row_number, $column_number, $value, $cell_format]);
 	}
 }
 //Just a simple test, by no means comprehensive
 
-class XLSXWriterTest extends PHPUnit_Framework_TestCase
+class XLSXWriterTest extends TestCase
 {
     /**
      * @covers XLSXWriter::writeCell
diff --git a/xlsxwriter.class.php b/xlsxwriter.class.php
index 6cdc077cfca4664b3d1a00137c2c65c05111ee42..ee1ae299879f76e470bcf9dbb514566468fe70dc 100644
--- a/xlsxwriter.class.php
+++ b/xlsxwriter.class.php
@@ -9,7 +9,7 @@ class XLSXWriter
 {
 	//------------------------------------------------------------------
 	//http://office.microsoft.com/en-us/excel-help/excel-specifications-and-limits-HP010073849.aspx
-	const EXCEL_2007_MAX_ROW=1048576; 
+	const EXCEL_2007_MAX_ROW=1048576;
 	const EXCEL_2007_MAX_COL=16384;
 	//------------------------------------------------------------------
 	protected $author ='Doc Author';
@@ -20,6 +20,46 @@ class XLSXWriter
 
 	protected $current_sheet = '';
 
+	protected $title;
+	protected $subject;
+	protected $company;
+	protected $description;
+	protected $keywords = [];
+
+	protected $cell_styles = [];
+
+	private const CELL_STYLES = [
+		'money' => 1,
+		'dollar' => 1,
+		'datetime' => 2,
+		'date' => 3,
+		'string' => 0,
+		'number.2' => 4,
+		'number.4' => 5,
+		'blackheader' => 6
+	];
+	private const ALLOWED_BORDERS = [
+		'left',
+		'right',
+		'top',
+		'bottom',
+	];
+	private const ALLOWED_BORDER_STYLES = [
+		'thin',
+		'medium',
+		'thick',
+		'dashDot',
+		'dashDotDot',
+		'dashed',
+		'dotted',
+		'double',
+		'hair',
+		'mediumDashDot',
+		'mediumDashDotDot',
+		'mediumDashed',
+		'slantDashDot',
+	];
+
 	public function __construct()
 	{
 		if(!ini_get('date.timezone'))
@@ -27,9 +67,38 @@ class XLSXWriter
 			//using date functions can kick out warning if this isn't set
 			date_default_timezone_set('UTC');
 		}
+
+
+		$this->addCellStyle($number_format='GENERAL', $style_string=null);
+		$this->addCellStyle($number_format='GENERAL', $style_string=null);
+		$this->addCellStyle($number_format='GENERAL', $style_string=null);
+		$this->addCellStyle($number_format='GENERAL', $style_string=null);
+		$this->addCellStyle($number_format='string', $style_string=null);
+		$this->addCellStyle($number_format='money', $style_string=null);
+		$this->addCellStyle($number_format='dollar', $style_string=null);
+		$this->addCellStyle($number_format='datetime', $style_string=null);
+		$this->addCellStyle($number_format='date', $style_string=null);
+		$this->addCellStyle($number_format='number.2', $style_string=null);
+		$this->addCellStyle($number_format='number.4', $style_string=null);
 	}
 
 	public function setAuthor($author='') { $this->author=$author; }
+	public function setTitle($title='') { $this->title=$title; }
+	public function setSubject($subject='') { $this->subject=$subject; }
+	public function setCompany($company='') { $this->company=$company; }
+	public function setKeywords($keywords=[]) { $this->keywords=$keywords; }
+	public function setDescription($description='') { $this->description=$description; }
+
+	public function getFileProperties(): array {
+		return [
+			"author" => $this->author,
+			"title" => $this->title,
+			"subject" => $this->subject,
+			"company" => $this->company,
+			"keywords" => $this->keywords,
+			"description" => $this->description,
+		];
+	}
 
 	public function __destruct()
 	{
@@ -72,7 +141,7 @@ class XLSXWriter
 		$zip = new ZipArchive();
 		if (empty($this->sheets))                       { self::log("Error in ".__CLASS__."::".__FUNCTION__.", no worksheets defined."); return; }
 		if (!$zip->open($filename, ZipArchive::CREATE)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", unable to create zip."); return; }
-		
+
 		$zip->addEmptyDir("docProps/");
 		$zip->addFromString("docProps/app.xml" , self::buildAppXML() );
 		$zip->addFromString("docProps/core.xml", self::buildCoreXML());
@@ -96,7 +165,7 @@ class XLSXWriter
 		$zip->close();
 	}
 
-	protected function initializeSheet($sheet_name)
+	protected function initializeSheet($sheet_name, $col_widths = [], $freeze_rows=false, $freeze_columns=false)
 	{
 		//if already initialized
 		if ($this->current_sheet==$sheet_name || isset($this->sheets[$sheet_name]))
@@ -104,17 +173,20 @@ class XLSXWriter
 
 		$sheet_filename = $this->tempFilename();
 		$sheet_xmlname = 'sheet' . (count($this->sheets) + 1).".xml";
-		$this->sheets[$sheet_name] = (object)array(
-			'filename' => $sheet_filename, 
-			'sheetname' => $sheet_name, 
+		$this->sheets[$sheet_name] = (object)[
+			'filename' => $sheet_filename,
+			'sheetname' => $sheet_name,
 			'xmlname' => $sheet_xmlname,
 			'row_count' => 0,
 			'file_writer' => new XLSXWriter_BuffererWriter($sheet_filename),
-			'cell_formats' => array(),
+			'cell_formats' => [],
+			'merge_cells' => [],
 			'max_cell_tag_start' => 0,
 			'max_cell_tag_end' => 0,
+			'freeze_rows' => $freeze_rows,
+			'freeze_columns' => $freeze_columns,
 			'finalized' => false,
-		);
+		];
 		$sheet = &$this->sheets[$sheet_name];
 		$tabselected = count($this->sheets) == 1 ? 'true' : 'false';//only first sheet is selected
 		$max_cell=XLSXWriter::xlsCell(self::EXCEL_2007_MAX_ROW, self::EXCEL_2007_MAX_COL);//XFE1048577
@@ -128,55 +200,118 @@ class XLSXWriter
 		$sheet->max_cell_tag_end = $sheet->file_writer->ftell();
 		$sheet->file_writer->write(  '<sheetViews>');
 		$sheet->file_writer->write(    '<sheetView colorId="64" defaultGridColor="true" rightToLeft="false" showFormulas="false" showGridLines="true" showOutlineSymbols="true" showRowColHeaders="true" showZeros="true" tabSelected="' . $tabselected . '" topLeftCell="A1" view="normal" windowProtection="false" workbookViewId="0" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100">');
-		$sheet->file_writer->write(      '<selection activeCell="A1" activeCellId="0" pane="topLeft" sqref="A1"/>');
+
+		if ($sheet->freeze_rows !== false && $sheet->freeze_columns !== false) {
+			$sheet->file_writer->write(      '<pane ySplit="'.$sheet->freeze_rows.'" xSplit="'.$sheet->freeze_columns.'" topLeftCell="'.self::xlsCell($sheet->freeze_rows, $sheet->freeze_columns).'" activePane="bottomRight" state="frozen"/>');
+			$sheet->file_writer->write(      '<selection activeCell="'.self::xlsCell($sheet->freeze_rows, 0).'" activeCellId="0" pane="topRight" sqref="'.self::xlsCell($sheet->freeze_rows, 0).'"/>');
+			$sheet->file_writer->write(      '<selection activeCell="'.self::xlsCell(0, $sheet->freeze_columns).'" activeCellId="0" pane="bottomLeft" sqref="'.self::xlsCell(0, $sheet->freeze_columns).'"/>');
+			$sheet->file_writer->write(      '<selection activeCell="'.self::xlsCell($sheet->freeze_rows, $sheet->freeze_columns).'" activeCellId="0" pane="bottomRight" sqref="'.self::xlsCell($sheet->freeze_rows, $sheet->freeze_columns).'"/>');
+		} elseif ($sheet->freeze_rows !== false) {
+			$sheet->file_writer->write(      '<pane ySplit="'.$sheet->freeze_rows.'" topLeftCell="'.self::xlsCell($sheet->freeze_rows, 0).'" activePane="bottomLeft" state="frozen"/>');
+			$sheet->file_writer->write(      '<selection activeCell="'.self::xlsCell($sheet->freeze_rows, 0).'" activeCellId="0" pane="bottomLeft" sqref="'.self::xlsCell($sheet->freeze_rows, 0).'"/>');
+		} elseif ($sheet->freeze_columns !== false) {
+			$sheet->file_writer->write(      '<pane xSplit="'.$sheet->freeze_columns.'" topLeftCell="'.self::xlsCell(0, $sheet->freeze_columns).'" activePane="topRight" state="frozen"/>');
+			$sheet->file_writer->write(      '<selection activeCell="'.self::xlsCell(0, $sheet->freeze_columns).'" activeCellId="0" pane="topRight" sqref="'.self::xlsCell(0, $sheet->freeze_columns).'"/>');
+		} else { // not frozen
+			$sheet->file_writer->write(      '<selection activeCell="A1" activeCellId="0" pane="topLeft" sqref="A1"/>');
+		}
+
 		$sheet->file_writer->write(    '</sheetView>');
 		$sheet->file_writer->write(  '</sheetViews>');
 		$sheet->file_writer->write(  '<cols>');
-		$sheet->file_writer->write(    '<col collapsed="false" hidden="false" max="1025" min="1" style="0" width="11.5"/>');
+
+		$cols_count = count($col_widths);
+
+		if (!empty($col_widths)) {
+			foreach($col_widths as $i => $column_width) {
+				$sheet->file_writer->write(    '<col collapsed="false" hidden="false" max="'. ($i + 1) .'" min="'. ($i + 1) .'" style="0" customWidth="true" width="'.floatval($column_width).'"/>');
+			}
+		}
+		$sheet->file_writer->write(    '<col collapsed="false" hidden="false" max="1024" min="'. ($cols_count + 1) .'" style="0" customWidth="false" width="11.5"/>');
+
 		$sheet->file_writer->write(  '</cols>');
 		$sheet->file_writer->write(  '<sheetData>');
 	}
 
-	public function writeSheetHeader($sheet_name, array $header_types)
-	{
-		if (empty($sheet_name) || empty($header_types) || !empty($this->sheets[$sheet_name]))
+	public function writeSheetHeader($sheet_name, array $header_types, $format = 'xlsx', $delimiter = ';', $subheader = NULL, $col_options = []) {
+		if (empty($sheet_name) || empty($header_types) || !empty($this->sheets[$sheet_name])) {
 			return;
+		}
+		if ($format == 'csv') {
+			if (!empty($subheader)) {
+				$return = '';
+				$return .= $this->writeCSVLine([' '], NULL, $delimiter);
+				$return .= $this->writeCSVLine([$subheader], NULL, $delimiter);
+				$return .= $this->writeCSVLine([' '], NULL, $delimiter);
+				$return .= $this->writeCSVLine($header_types, true, $delimiter);
+				return $return;
+			}
+			return $this->writeCSVLine($header_types, true, $delimiter);
+		}
+		if (!empty($subheader)) {
+			$this->writeSheetRow($sheet_name, ['   '], $format, $delimiter);
+			$this->writeSheetRow($sheet_name, [$subheader], $format, $delimiter);
+			$this->writeSheetRow($sheet_name, ['   '], $format, $delimiter);
+			$start = 3;
+		} else {
+			$start = 0;
+		}
 
-		self::initializeSheet($sheet_name);
+		$col_widths = (!empty($col_options['widths'])) ? (array)$col_options['widths'] : [];
+		$freeze_rows = (array_key_exists('freeze_rows', $col_options) && $col_options['freeze_rows'] !== false) ? intval($col_options['freeze_rows']) : false;
+		$freeze_columns = (array_key_exists('freeze_columns', $col_options) && $col_options['freeze_columns'] !== false) ? intval($col_options['freeze_columns']) : false;
+
+		self::initializeSheet($sheet_name, $col_widths, $freeze_rows, $freeze_columns);
 		$sheet = &$this->sheets[$sheet_name];
 		$sheet->cell_formats = array_values($header_types);
 		$header_row = array_keys($header_types);
 
-		$sheet->file_writer->write('<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="' . (1) . '">');
+		$sheet->file_writer->write('<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="' . ($start + 1) . '">');
+
+		$header_style = ['fill_idx' => '2', 'font_idx' => '4'];
+		$cell_style_index = $this->addCellStyle($number_format='blackheader', json_encode($header_style));
 		foreach ($header_row as $k => $v) {
-			$this->writeCell($sheet->file_writer, 0, $k, $v, $cell_format = 'string');
+			$this->writeCell($sheet->file_writer, $start, $k, $v, 'blackheader', $cell_style_index);
 		}
 		$sheet->file_writer->write('</row>');
 		$sheet->row_count++;
 		$this->current_sheet = $sheet_name;
 	}
 
-	public function writeSheetRow($sheet_name, array $row)
-	{
-		if (empty($sheet_name) || empty($row))
+	public function writeSheetRow($sheet_name, array $row, $format = 'xlsx', $delimiter = ';', array $row_options = []) {
+		if (empty($sheet_name) || empty($row)) {
 			return;
+		}
+		if ($format == 'csv') {
+			return $this->writeCSVLine($row, NULL, $delimiter);
+		}
 
 		self::initializeSheet($sheet_name);
 		$sheet = &$this->sheets[$sheet_name];
-		if (empty($sheet->cell_formats))
-		{
+		if (empty($sheet->cell_formats)) {
 			$sheet->cell_formats = array_fill(0, count($row), 'string');
 		}
 
-		$sheet->file_writer->write('<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="' . ($sheet->row_count + 1) . '">');
-		foreach ($row as $k => $v) {
-			$this->writeCell($sheet->file_writer, $sheet->row_count, $k, $v, $sheet->cell_formats[$k]);
+		$ht = array_key_exists('height', $row_options) ? floatval($row_options['height']) : 12.1;
+		$customHt = array_key_exists('height', $row_options) ? 'true' : 'false';
+		$hidden = (array_key_exists('hidden', $row_options) && $row_options['hidden']) ? 'true' : 'false';
+		$collapsed = (array_key_exists('collapsed', $row_options) && $row_options['collapsed']) ? 'true' : 'false';
+		$sheet->file_writer->write('<row collapsed="'.($collapsed).'" customFormat="false" customHeight="'.($customHt).'" hidden="'.($hidden).'" ht="'.($ht).'" outlineLevel="0" r="' . ($sheet->row_count + 1) . '">');
+
+		$style = &$row_options;
+		foreach ($row as $column => $content) {
+			$cell_value = (is_array($content)) ? $content[0] : $content;
+			$number_format_type = (is_array($content)) ? $content[1] : $sheet->cell_formats[$column];
+			$style_string = (!empty($style)) ? json_encode((isset($style[0])) ? $style[$column] : $style) : null;
+			$cell_style_idx = $this->addCellStyle($number_format_type, $style_string);
+			$this->writeCell($sheet->file_writer, $sheet->row_count, $column, $cell_value, $number_format_type, $cell_style_idx);
 		}
+
 		$sheet->file_writer->write('</row>');
 		$sheet->row_count++;
 		$this->current_sheet = $sheet_name;
 	}
-	
+
 	protected function finalizeSheet($sheet_name)
 	{
 		if (empty($sheet_name) || $this->sheets[$sheet_name]->finalized)
@@ -185,6 +320,15 @@ class XLSXWriter
 		$sheet = &$this->sheets[$sheet_name];
 
 		$sheet->file_writer->write(    '</sheetData>');
+
+		if (!empty($sheet->merge_cells)) {
+			$sheet->file_writer->write('<mergeCells>');
+			foreach ($sheet->merge_cells as $range) {
+				$sheet->file_writer->write('<mergeCell ref="' . $range . '"/>');
+			}
+			$sheet->file_writer->write('</mergeCells>');
+		}
+
 		$sheet->file_writer->write(    '<printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"/>');
 		$sheet->file_writer->write(    '<pageMargins left="0.5" right="0.5" top="1.0" bottom="1.0" header="0.5" footer="0.5"/>');
 		$sheet->file_writer->write(    '<pageSetup blackAndWhite="false" cellComments="none" copies="1" draft="false" firstPageNumber="1" fitToHeight="1" fitToWidth="1" horizontalDpi="300" orientation="portrait" pageOrder="downThenOver" paperSize="1" scale="100" useFirstPageNumber="true" usePrinterDefaults="false" verticalDpi="300"/>');
@@ -203,6 +347,43 @@ class XLSXWriter
 		$sheet->finalized=true;
 	}
 
+	public function markMergedCell($sheet_name, $start_cell_row, $start_cell_column, $end_cell_row, $end_cell_column, $format = 'xlsx') {
+		if (empty($sheet_name) || $this->sheets[$sheet_name]->finalized || $format == 'csv'){
+			return;
+		}
+
+		self::initializeSheet($sheet_name);
+
+		$sheet = &$this->sheets[$sheet_name];
+		$startCell = self::xlsCell($start_cell_row, $start_cell_column);
+		$endCell = self::xlsCell($end_cell_row, $end_cell_column);
+
+		$sheet->merge_cells[] = $startCell . ":" . $endCell;
+	}
+
+	public function writeCSV(array $data, array $header_types=array(), $delimiter = ';') {
+		$header_text = array_keys($header_types);
+
+		$output = '';
+		$output .= implode($delimiter, $header_text) . "\n";
+		$output .= implode("\n", array_map(function($array) use (&$delimiter) {
+			return implode($delimiter, $array);
+		}, $data));
+		return $output;
+	}
+
+	public function writeCSVLine(array $data, $type = null, $delimiter = ';') {
+		if (!empty($type)) {
+			$text = array_keys($data);
+		} else {
+			$text = $data;
+		}
+
+		$output = '';
+		$output .= implode($delimiter, $text) . "\n";
+		echo $output;
+	}
+
 	public function writeSheet(array $data, $sheet_name='', array $header_types=array() )
 	{
 		$sheet_name = empty($sheet_name) ? 'Sheet1' : $sheet_name;
@@ -218,50 +399,100 @@ class XLSXWriter
 		$this->finalizeSheet($sheet_name);
 	}
 
-	protected function writeCell(XLSXWriter_BuffererWriter &$file, $row_number, $column_number, $value, $cell_format)
+	protected function getCellFormat($cell_format) {
+		return isset(self::CELL_STYLES[$cell_format]) ? self::CELL_STYLES[$cell_format] : '0';
+	}
+
+	private function addCellStyle($cell_format, $cell_style_string) {
+		$cell_format_idx = $this->getCellFormat($cell_format);
+		$lookup_string = $cell_format_idx.";".$cell_style_string;
+		$cell_style_idx = self::add_to_list_get_index($this->cell_styles, $lookup_string);
+
+		return $cell_style_idx;
+	}
+
+	protected function writeCell(XLSXWriter_BuffererWriter &$file, $row_number, $column_number, $value, $cell_format, $cell_style_idx = null)
 	{
-		static $styles = array('money'=>1,'dollar'=>1,'datetime'=>2,'date'=>3,'string'=>0);
-		$cell = self::xlsCell($row_number, $column_number);
-		$s = isset($styles[$cell_format]) ? $styles[$cell_format] : '0';
+		$cell_name = self::xlsCell($row_number, $column_number);
+		if (is_null($cell_style_idx)) {
+			$cell_style_idx = $this->addCellStyle($cell_format, null);
+		}
 
 		if (!is_scalar($value) || $value==='') { //objects, array, empty
-			$file->write('<c r="'.$cell.'" s="'.$s.'"/>');
+			$file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'"/>');
+		} elseif (preg_match('#^number\.[0-9]$#', $cell_format)) {
+			$file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'" t="n"><v>'.$value.'</v></c>');
 		} elseif ($cell_format=='date') {
-			$file->write('<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.intval(self::convert_date_time($value)).'</v></c>');
+			$file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'" t="n"><v>'.intval(self::convert_date_time($value)).'</v></c>');
 		} elseif ($cell_format=='datetime') {
-			$file->write('<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.self::convert_date_time($value).'</v></c>');
+			$file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'" t="n"><v>'.self::convert_date_time($value).'</v></c>');
 		} elseif (!is_string($value)) {
-			$file->write('<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.($value*1).'</v></c>');//int,float, etc
-		} elseif ($value{0}!='0' && filter_var($value, FILTER_VALIDATE_INT)){ //excel wants to trim leading zeros
-			$file->write('<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.($value).'</v></c>');//numeric string
-		} elseif ($value{0}=='='){
-			$file->write('<c r="'.$cell.'" s="'.$s.'" t="s"><f>'.self::xmlspecialchars($value).'</f></c>');
+			$file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'" t="n"><v>'.($value*1).'</v></c>');//int,float, etc
+		} elseif ($value[0]!='0' && filter_var($value, FILTER_VALIDATE_INT)){ //excel wants to trim leading zeros
+			$file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'" t="n"><v>'.($value).'</v></c>');//numeric string
+		} elseif ($value[0]=='='){
+			$file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'" t="s"><f>'.self::xmlspecialchars($value).'</f></c>');
 		} elseif ($value!==''){
-			$file->write('<c r="'.$cell.'" s="'.$s.'" t="s"><v>'.self::xmlspecialchars($this->setSharedString($value)).'</v></c>');
+			$file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'" t="s"><v>'.self::xmlspecialchars($this->setSharedString($value)).'</v></c>');
 		}
 	}
 
 	protected function writeStylesXML()
 	{
+		$styles = $this->spreadStyles();
+		$borders = $styles['borders'];
+		$cell_styles = $styles['styles'];
+
 		$temporary_filename = $this->tempFilename();
 		$file = new XLSXWriter_BuffererWriter($temporary_filename);
 		$file->write('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
 		$file->write('<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">');
-		$file->write('<numFmts count="4">');
+		$file->write('<numFmts count="6">');
 		$file->write(		'<numFmt formatCode="GENERAL" numFmtId="164"/>');
 		$file->write(		'<numFmt formatCode="[$$-1009]#,##0.00;[RED]\-[$$-1009]#,##0.00" numFmtId="165"/>');
 		$file->write(		'<numFmt formatCode="YYYY-MM-DD\ HH:MM:SS" numFmtId="166"/>');
 		$file->write(		'<numFmt formatCode="YYYY-MM-DD" numFmtId="167"/>');
+		$file->write(		'<numFmt formatCode="#,##0.00" numFmtId="168"/>');
+		$file->write(		'<numFmt formatCode="0.0000" numFmtId="169"/>');
 		$file->write('</numFmts>');
-		$file->write('<fonts count="4">');
+		$file->write('<fonts count="5">');
 		$file->write(		'<font><name val="Arial"/><charset val="1"/><family val="2"/><sz val="10"/></font>');
 		$file->write(		'<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
 		$file->write(		'<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
 		$file->write(		'<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
+		$file->write(		'<font><name val="Arial"/><b val="true"/><charset val="1"/><family val="2"/><sz val="10"/></font>');
 		$file->write('</fonts>');
-		$file->write('<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>');
-		$file->write('<borders count="1"><border diagonalDown="false" diagonalUp="false"><left/><right/><top/><bottom/><diagonal/></border></borders>');
-		$file->write(	'<cellStyleXfs count="20">');
+		$file->write('<fills count="3">');
+		$file->write('		<fill><patternFill patternType="none"/></fill>');
+		$file->write('		<fill><patternFill patternType="gray125"/></fill>');
+		$file->write('		<fill><patternFill patternType="solid"><fgColor theme="0" tint="-0.249977111117893"/><bgColor rgb="FF003300"/></patternFill></fill>');
+		$file->write('</fills>');
+
+		$file->write('<borders count="'.(count($borders)).'">');
+		$file->write(    '<border diagonalDown="false" diagonalUp="false"><left/><right/><top/><bottom/><diagonal/></border>');
+		foreach($borders as $border) {
+			if (empty($border)) {
+				continue;
+			}
+
+			$pieces = json_decode($border,true);
+
+			$border_style = !empty($pieces['style']) ? $pieces['style'] : 'hair';
+			$border_color = !empty($pieces['color']) ? '<color rgb="'.strval($pieces['color']).'"/>' : '';
+
+			$file->write('<border diagonalDown="false" diagonalUp="false">');
+
+			foreach (self::ALLOWED_BORDERS as $side) {
+				$show_side = in_array($side,$pieces['side']) ? true : false;
+				$file->write($show_side ? "<$side style=\"$border_style\">$border_color</$side>" : "<$side/>");
+			}
+
+			$file->write(  '<diagonal/>');
+			$file->write('</border>');
+		}
+		$file->write('</borders>');
+
+		$file->write('<cellStyleXfs count="20">');
 		$file->write(		'<xf applyAlignment="true" applyBorder="true" applyFont="true" applyProtection="true" borderId="0" fillId="0" fontId="0" numFmtId="164">');
 		$file->write(		'<alignment horizontal="general" indent="0" shrinkToFit="false" textRotation="0" vertical="bottom" wrapText="false"/>');
 		$file->write(		'<protection hidden="false" locked="true"/>');
@@ -285,13 +516,26 @@ class XLSXWriter
 		$file->write(		'<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="44"/>');
 		$file->write(		'<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="42"/>');
 		$file->write(		'<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"/>');
-		$file->write(	'</cellStyleXfs>');
-		$file->write(	'<cellXfs count="4">');
-		$file->write(		'<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"/>');
-		$file->write(		'<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="165" xfId="0"/>');
-		$file->write(		'<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="166" xfId="0"/>');
-		$file->write(		'<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="167" xfId="0"/>');
-		$file->write(	'</cellXfs>');
+		$file->write('</cellStyleXfs>');
+
+		$file->write('<cellXfs count="'.(count($cell_styles)).'">');
+		foreach($cell_styles as $cell_style) {
+			$applyAlignment = isset($cell_style['alignment']) ? 'true' : 'false';
+			$wrapText = !empty($cell_style['wrap_text']) ? 'true' : 'false';
+			$horizAlignment = isset($cell_style['halign']) ? $cell_style['halign'] : 'general';
+			$vertAlignment = isset($cell_style['valign']) ? $cell_style['valign'] : 'bottom';
+			$applyBorder = isset($cell_style['border_idx']) ? 'true' : 'false';
+			$applyFont = 'true';
+			$borderIdx = isset($cell_style['border_idx']) ? intval($cell_style['border_idx']) : 0;
+			$fillIdx = isset($cell_style['fill_idx']) ? intval($cell_style['fill_idx']) : 0;
+			$fontIdx = isset($cell_style['font_idx']) ? intval($cell_style['font_idx']) : 0;
+			$file->write('<xf applyAlignment="'.$applyAlignment.'" applyBorder="'.$applyBorder.'" applyFont="'.$applyFont.'" applyProtection="false" borderId="'.($borderIdx).'" fillId="'.($fillIdx).'" fontId="'.($fontIdx).'" numFmtId="'.(164+$cell_style['num_fmt_idx']).'" xfId="0">');
+			$file->write('	<alignment horizontal="'.$horizAlignment.'" vertical="'.$vertAlignment.'" textRotation="0" wrapText="'.$wrapText.'" indent="0" shrinkToFit="false"/>');
+			$file->write('	<protection locked="true" hidden="false"/>');
+			$file->write('</xf>');
+		}
+		$file->write('</cellXfs>');
+
 		$file->write(	'<cellStyles count="6">');
 		$file->write(		'<cellStyle builtinId="0" customBuiltin="false" name="Normal" xfId="0"/>');
 		$file->write(		'<cellStyle builtinId="3" customBuiltin="false" name="Comma" xfId="15"/>');
@@ -301,10 +545,46 @@ class XLSXWriter
 		$file->write(		'<cellStyle builtinId="5" customBuiltin="false" name="Percent" xfId="19"/>');
 		$file->write(	'</cellStyles>');
 		$file->write('</styleSheet>');
+
 		$file->close();
 		return $temporary_filename;
 	}
 
+	protected function spreadStyles() {
+		$borders = [''];//1 placeholder for static xml later
+		$style_indexes = [];
+
+		foreach ($this->cell_styles as $i => $cell_style_string) {
+			[$number_format_idx, $style_json_string] = explode(";", $cell_style_string, 2);
+			$style = json_decode($style_json_string, $as_assoc = true);
+
+			$style_indexes[$i] = $style ?? [];
+			$style_indexes[$i]['num_fmt_idx'] = $number_format_idx;
+			if (isset($style['border']) && is_string($style['border'])) { //border is a comma delimited str
+				$border_value['side'] = array_intersect(explode(",", $style['border']), self::ALLOWED_BORDERS);
+
+				if (isset($style['border-style']) && in_array($style['border-style'], self::ALLOWED_BORDER_STYLES)) {
+					$border_value['style'] = $style['border-style'];
+				}
+
+				if (isset($style['border-color']) && is_string($style['border-color']) && $style['border-color'][0] == '#') {
+					$hexa_border_color = substr($style['border-color'], 1, 6);
+					$hexa_border_color = strlen($hexa_border_color) == 3 ? $hexa_border_color[0] . $hexa_border_color[0] .
+						$hexa_border_color[1] . $hexa_border_color[1] .
+						$hexa_border_color[2] . $hexa_border_color[2] : $hexa_border_color;// expand cf0 => ccff00
+					$border_value['color'] = "FF" . strtoupper($hexa_border_color);
+				}
+
+				$style_indexes[$i]['border_idx'] = self::add_to_list_get_index($borders, json_encode($border_value));
+			}
+		}
+
+		return [
+			'borders' => $borders,
+			'styles' => $style_indexes,
+		];
+	}
+
 	protected function setSharedString($v)
 	{
 		if (isset($this->shared_strings[$v]))
@@ -332,7 +612,7 @@ class XLSXWriter
 		}
 		$file->write('</sst>');
 		$file->close();
-		
+
 		return $temporary_filename;
 	}
 
@@ -340,7 +620,10 @@ class XLSXWriter
 	{
 		$app_xml="";
 		$app_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
-		$app_xml.='<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><TotalTime>0</TotalTime></Properties>';
+		$app_xml.='<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">';
+		$app_xml.='<TotalTime>0</TotalTime>';
+		$app_xml.='<Company>'.self::xmlspecialchars($this->company).'</Company>';
+		$app_xml.='</Properties>';
 		return $app_xml;
 	}
 
@@ -350,7 +633,13 @@ class XLSXWriter
 		$core_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
 		$core_xml.='<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">';
 		$core_xml.='<dcterms:created xsi:type="dcterms:W3CDTF">'.date("Y-m-d\TH:i:s.00\Z").'</dcterms:created>';//$date_time = '2014-10-25T15:54:37.00Z';
+		$core_xml.='<dc:title>'.self::xmlspecialchars($this->title).'</dc:title>';
+		$core_xml.='<dc:subject>'.self::xmlspecialchars($this->subject).'</dc:subject>';
 		$core_xml.='<dc:creator>'.self::xmlspecialchars($this->author).'</dc:creator>';
+		if (!empty($this->keywords)) {
+			$core_xml.='<cp:keywords>'.self::xmlspecialchars(implode (", ", (array)$this->keywords)).'</cp:keywords>';
+		}
+		$core_xml.='<dc:description>'.self::xmlspecialchars($this->description).'</dc:description>';
 		$core_xml.='<cp:revision>0</cp:revision>';
 		$core_xml.='</cp:coreProperties>';
 		return $core_xml;
@@ -487,7 +776,7 @@ class XLSXWriter
 		}
 
 		//using 1900 as epoch, not 1904, ignoring 1904 special case
-		
+
 		# Special cases for Excel.
 		if ("$year-$month-$day"=='1899-12-31')  return $seconds      ;    # Excel 1900 epoch
 		if ("$year-$month-$day"=='1900-01-00')  return $seconds      ;    # Excel 1900 epoch
@@ -526,6 +815,16 @@ class XLSXWriter
 		return $days + $seconds;
 	}
 	//------------------------------------------------------------------
+	public static function add_to_list_get_index(&$haystack, $needle) {
+		$existing_idx = array_search($needle, $haystack, $strict=true);
+
+		if ($existing_idx === false) {
+			$existing_idx = count($haystack);
+			$haystack[] = $needle;
+		}
+
+		return $existing_idx;
+	}
 }
 
 class XLSXWriter_BuffererWriter
@@ -572,11 +871,11 @@ class XLSXWriter_BuffererWriter
 		}
 	}
 
-	public function __destruct() 
+	public function __destruct()
 	{
 		$this->close();
 	}
-	
+
 	public function ftell()
 	{
 		if ($this->fd) {