diff --git a/docs/topics/reading-and-writing-to-file.md b/docs/topics/reading-and-writing-to-file.md index c0dc7c033e..8f92e1f232 100644 --- a/docs/topics/reading-and-writing-to-file.md +++ b/docs/topics/reading-and-writing-to-file.md @@ -681,35 +681,26 @@ Supported methods: - `generateStyles()` - `generateSheetData()` - `generateHTMLFooter()` +- `generateHTMLAll()` Here's an example which retrieves all parts independently and merges them into a resulting HTML page: ``` php -generateHTMLHeader(); -?> - - +$newstyle = << +$sty html { - font-family: Times New Roman; - font-size: 9pt; - background-color: white; + background-color: yellow; } - -generateStyles(false); // do not write -?> - ---> - -@', "$newstyle\n", $hdr); echo $writer->generateSheetData(); echo $writer->generateHTMLFooter(); -?> ``` #### Writing UTF-8 HTML files diff --git a/samples/Basic/17a_Html.php b/samples/Basic/17a_Html.php new file mode 100644 index 0000000000..20578fbe36 --- /dev/null +++ b/samples/Basic/17a_Html.php @@ -0,0 +1,14 @@ +getFilename(__FILE__, 'html'); +$writer = new Html($spreadsheet); + +$callStartTime = microtime(true); +$writer->setEmbedImages(true); +$writer->save($filename); +$helper->logWrite($writer, $filename, $callStartTime); diff --git a/samples/Basic/25_In_memory_image.php b/samples/Basic/25_In_memory_image.php index a897486dfd..5985a0a076 100644 --- a/samples/Basic/25_In_memory_image.php +++ b/samples/Basic/25_In_memory_image.php @@ -35,6 +35,7 @@ $drawing->setMimeType(MemoryDrawing::MIMETYPE_DEFAULT); $drawing->setHeight(36); $drawing->setWorksheet($spreadsheet->getActiveSheet()); +$drawing->setCoordinates('C5'); // Save $helper->write($spreadsheet, __FILE__, ['Xlsx', 'Html']); diff --git a/src/PhpSpreadsheet/Style/NumberFormat.php b/src/PhpSpreadsheet/Style/NumberFormat.php index 079e1d20dc..1f4a6c0c74 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat.php +++ b/src/PhpSpreadsheet/Style/NumberFormat.php @@ -537,7 +537,15 @@ private static function formatAsFraction(&$value, &$format) $adjustedDecimalPart = $decimalPart / $GCD; $adjustedDecimalDivisor = $decimalDivisor / $GCD; - if ((strpos($format, '0') !== false) || (strpos($format, '#') !== false) || (substr($format, 0, 3) == '? ?')) { + if ((strpos($format, '0') !== false)) { + $value = "$sign$integerPart $adjustedDecimalPart/$adjustedDecimalDivisor"; + } elseif ((strpos($format, '#') !== false)) { + if ($integerPart == 0) { + $value = "$sign$adjustedDecimalPart/$adjustedDecimalDivisor"; + } else { + $value = "$sign$integerPart $adjustedDecimalPart/$adjustedDecimalDivisor"; + } + } elseif ((substr($format, 0, 3) == '? ?')) { if ($integerPart == 0) { $integerPart = ''; } @@ -653,9 +661,12 @@ private static function formatStraightNumericValue($value, $format, array $match private static function formatAsNumber($value, $format) { - if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) { - return 'EUR ' . sprintf('%1.2f', $value); - } + // The "_" in this string has already been stripped out, + // so this test is never true. Furthermore, testing + // on Excel shows this format uses Euro symbol, not "EUR". + //if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) { + // return 'EUR ' . sprintf('%1.2f', $value); + //} // Some non-number strings are quoted, so we'll get rid of the quotes, likewise any positional * symbols $format = str_replace(['"', '*'], '', $format); @@ -717,6 +728,89 @@ private static function formatAsNumber($value, $format) return $value; } + private static function splitFormatCompare($value, $cond, $val, $dfcond, $dfval) + { + if (!$cond) { + $cond = $dfcond; + $val = $dfval; + } + switch ($cond) { + case '>': + return $value > $val; + + case '<': + return $value < $val; + + case '<=': + return $value <= $val; + + case '<>': + return $value != $val; + + case '=': + return $value == $val; + } + + return $value >= $val; + } + + private static function splitFormat($sections, $value) + { + // Extract the relevant section depending on whether number is positive, negative, or zero? + // Text not supported yet. + // Here is how the sections apply to various values in Excel: + // 1 section: [POSITIVE/NEGATIVE/ZERO/TEXT] + // 2 sections: [POSITIVE/ZERO/TEXT] [NEGATIVE] + // 3 sections: [POSITIVE/TEXT] [NEGATIVE] [ZERO] + // 4 sections: [POSITIVE] [NEGATIVE] [ZERO] [TEXT] + $cnt = count($sections); + $color_regex = '/\\[(' . implode('|', Color::NAMED_COLORS) . ')\\]/'; + $cond_regex = '/\\[(>|>=|<|<=|=|<>)([+-]?\\d+([.]\\d+)?)\\]/'; + $colors = ['', '', '', '', '']; + $condops = ['', '', '', '', '']; + $condvals = [0, 0, 0, 0, 0]; + for ($idx = 0; $idx < $cnt; ++$idx) { + if (preg_match($color_regex, $sections[$idx], $matches)) { + $colors[$idx] = $matches[0]; + $sections[$idx] = preg_replace($color_regex, '', $sections[$idx]); + } + if (preg_match($cond_regex, $sections[$idx], $matches)) { + $condops[$idx] = $matches[1]; + $condvals[$idx] = $matches[2]; + $sections[$idx] = preg_replace($cond_regex, '', $sections[$idx]); + } + } + $color = $colors[0]; + $format = $sections[0]; + $absval = $value; + switch ($cnt) { + case 2: + $absval = abs($value); + if (!self::splitFormatCompare($value, $condops[0], $condvals[0], '>=', 0)) { + $color = $colors[1]; + $format = $sections[1]; + } + + break; + case 3: + case 4: + $absval = abs($value); + if (!self::splitFormatCompare($value, $condops[0], $condvals[0], '>', 0)) { + if (self::splitFormatCompare($value, $condops[1], $condvals[1], '<', 0)) { + $color = $colors[1]; + $format = $sections[1]; + } else { + $color = $colors[2]; + $format = $sections[2]; + } + } + + break; + } + + return [$color, $format, $absval]; + } + /** * Convert a value in a pre-defined format to a PHP string. * @@ -745,50 +839,12 @@ public static function toFormattedString($value, $format, $callBack = null) // Get the sections, there can be up to four sections, separated with a semi-colon (but only if not a quoted literal) $sections = preg_split('/(;)(?=(?:[^"]|"[^"]*")*$)/u', $format); - // Extract the relevant section depending on whether number is positive, negative, or zero? - // Text not supported yet. - // Here is how the sections apply to various values in Excel: - // 1 section: [POSITIVE/NEGATIVE/ZERO/TEXT] - // 2 sections: [POSITIVE/ZERO/TEXT] [NEGATIVE] - // 3 sections: [POSITIVE/TEXT] [NEGATIVE] [ZERO] - // 4 sections: [POSITIVE] [NEGATIVE] [ZERO] [TEXT] - switch (count($sections)) { - case 1: - $format = $sections[0]; - - break; - case 2: - $format = ($value >= 0) ? $sections[0] : $sections[1]; - $value = abs($value); // Use the absolute value - break; - case 3: - $format = ($value > 0) ? - $sections[0] : (($value < 0) ? - $sections[1] : $sections[2]); - $value = abs($value); // Use the absolute value - break; - case 4: - $format = ($value > 0) ? - $sections[0] : (($value < 0) ? - $sections[1] : $sections[2]); - $value = abs($value); // Use the absolute value - break; - default: - // something is wrong, just use first section - $format = $sections[0]; - - break; - } + [$colors, $format, $value] = self::splitFormat($sections, $value); // In Excel formats, "_" is used to add spacing, // The following character indicates the size of the spacing, which we can't do in HTML, so we just use a standard space $format = preg_replace('/_./', ' ', $format); - // Save format with color information for later use below - $formatColor = $format; - // Strip colour information - $color_regex = '/\[(' . implode('|', Color::NAMED_COLORS) . ')\]/'; - $format = preg_replace($color_regex, '', $format); // Let's begin inspecting the format and converting the value to a formatted string // Check for date/time characters (not inside quotes) @@ -809,7 +865,7 @@ public static function toFormattedString($value, $format, $callBack = null) // Additional formatting provided by callback function if ($callBack !== null) { [$writerInstance, $function] = $callBack; - $value = $writerInstance->$function($value, $formatColor); + $value = $writerInstance->$function($value, $colors); } return $value; diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index ec75400144..bb5098069a 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -148,6 +148,23 @@ public function __construct(Spreadsheet $spreadsheet) * @param resource|string $pFilename */ public function save($pFilename) + { + // Open file + $this->openFileHandle($pFilename); + + // Write html + fwrite($this->fileHandle, $this->generateHTMLAll()); + + // Close file + $this->maybeCloseFileHandle(); + } + + /** + * Save Spreadsheet as html to variable. + * + * @return string + */ + public function generateHtmlAll() { // garbage collect $this->spreadsheet->garbageCollect(); @@ -160,29 +177,35 @@ public function save($pFilename) // Build CSS $this->buildCSS(!$this->useInlineCss); - // Open file - $this->openFileHandle($pFilename); + $html = ''; // Write headers - fwrite($this->fileHandle, $this->generateHTMLHeader(!$this->useInlineCss)); + $html .= $this->generateHTMLHeader(!$this->useInlineCss); // Write navigation (tabs) if ((!$this->isPdf) && ($this->generateSheetNavigationBlock)) { - fwrite($this->fileHandle, $this->generateNavigation()); + $html .= $this->generateNavigation(); } // Write data - fwrite($this->fileHandle, $this->generateSheetData()); + $html .= $this->generateSheetData(); // Write footer - fwrite($this->fileHandle, $this->generateHTMLFooter()); - - $this->maybeCloseFileHandle(); + $html .= $this->generateHTMLFooter(); Calculation::setArrayReturnType($saveArrayReturnType); Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog); + + return $html; } + const VALIGN_ARR = [ + Alignment::VERTICAL_BOTTOM => 'bottom', + Alignment::VERTICAL_TOP => 'top', + Alignment::VERTICAL_CENTER => 'middle', + Alignment::VERTICAL_JUSTIFY => 'middle', + ]; + /** * Map VAlign. * @@ -192,45 +215,44 @@ public function save($pFilename) */ private function mapVAlign($vAlign) { - switch ($vAlign) { - case Alignment::VERTICAL_BOTTOM: - return 'bottom'; - case Alignment::VERTICAL_TOP: - return 'top'; - case Alignment::VERTICAL_CENTER: - case Alignment::VERTICAL_JUSTIFY: - return 'middle'; - default: - return 'baseline'; - } + return array_key_exists($vAlign, self::VALIGN_ARR) ? self::VALIGN_ARR[$vAlign] : 'baseline'; } + const HALIGN_ARR = [ + Alignment::HORIZONTAL_LEFT => 'left', + Alignment::HORIZONTAL_RIGHT => 'right', + Alignment::HORIZONTAL_CENTER => 'center', + Alignment::HORIZONTAL_CENTER_CONTINUOUS => 'center', + Alignment::HORIZONTAL_JUSTIFY => 'justify', + ]; + /** * Map HAlign. * * @param string $hAlign Horizontal alignment * - * @return false|string + * @return string */ private function mapHAlign($hAlign) { - switch ($hAlign) { - case Alignment::HORIZONTAL_GENERAL: - return false; - case Alignment::HORIZONTAL_LEFT: - return 'left'; - case Alignment::HORIZONTAL_RIGHT: - return 'right'; - case Alignment::HORIZONTAL_CENTER: - case Alignment::HORIZONTAL_CENTER_CONTINUOUS: - return 'center'; - case Alignment::HORIZONTAL_JUSTIFY: - return 'justify'; - default: - return false; - } + return array_key_exists($hAlign, self::HALIGN_ARR) ? self::HALIGN_ARR[$hAlign] : ''; } + const BORDER_ARR = [ + Border::BORDER_NONE => 'none', + Border::BORDER_DASHDOT => '1px dashed', + Border::BORDER_DASHDOTDOT => '1px dotted', + Border::BORDER_DASHED => '1px dashed', + Border::BORDER_DOTTED => '1px dotted', + Border::BORDER_DOUBLE => '3px double', + Border::BORDER_HAIR => '1px solid', + Border::BORDER_MEDIUM => '2px solid', + Border::BORDER_MEDIUMDASHDOT => '2px dashed', + Border::BORDER_MEDIUMDASHDOTDOT => '2px dotted', + Border::BORDER_SLANTDASHDOT => '2px dashed', + Border::BORDER_THICK => '3px solid', + ]; + /** * Map border style. * @@ -240,39 +262,7 @@ private function mapHAlign($hAlign) */ private function mapBorderStyle($borderStyle) { - switch ($borderStyle) { - case Border::BORDER_NONE: - return 'none'; - case Border::BORDER_DASHDOT: - return '1px dashed'; - case Border::BORDER_DASHDOTDOT: - return '1px dotted'; - case Border::BORDER_DASHED: - return '1px dashed'; - case Border::BORDER_DOTTED: - return '1px dotted'; - case Border::BORDER_DOUBLE: - return '3px double'; - case Border::BORDER_HAIR: - return '1px solid'; - case Border::BORDER_MEDIUM: - return '2px solid'; - case Border::BORDER_MEDIUMDASHDOT: - return '2px dashed'; - case Border::BORDER_MEDIUMDASHDOTDOT: - return '2px dotted'; - case Border::BORDER_MEDIUMDASHED: - return '2px dashed'; - case Border::BORDER_SLANTDASHDOT: - return '2px dashed'; - case Border::BORDER_THICK: - return '3px solid'; - case Border::BORDER_THIN: - return '1px solid'; - default: - // map others to thin - return '1px solid'; - } + return array_key_exists($borderStyle, self::BORDER_ARR) ? self::BORDER_ARR[$borderStyle] : '1px solid'; } /** @@ -335,6 +325,11 @@ public function writeAllSheets() return $this; } + private static function generateMeta($val, $desc) + { + return $val ? (' ' . PHP_EOL) : ''; + } + /** * Generate HTML header. * @@ -346,42 +341,22 @@ public function generateHTMLHeader($pIncludeStyles = false) { // Construct HTML $properties = $this->spreadsheet->getProperties(); - $html = '' . PHP_EOL; - $html .= '' . PHP_EOL; + $html = '' . PHP_EOL; + $html .= '' . PHP_EOL; $html .= ' ' . PHP_EOL; - $html .= ' ' . PHP_EOL; - $html .= ' ' . PHP_EOL; - if ($properties->getTitle() > '') { - $html .= ' ' . htmlspecialchars($properties->getTitle()) . '' . PHP_EOL; - } - if ($properties->getCreator() > '') { - $html .= ' ' . PHP_EOL; - } - if ($properties->getTitle() > '') { - $html .= ' ' . PHP_EOL; - } - if ($properties->getDescription() > '') { - $html .= ' ' . PHP_EOL; - } - if ($properties->getSubject() > '') { - $html .= ' ' . PHP_EOL; - } - if ($properties->getKeywords() > '') { - $html .= ' ' . PHP_EOL; - } - if ($properties->getCategory() > '') { - $html .= ' ' . PHP_EOL; - } - if ($properties->getCompany() > '') { - $html .= ' ' . PHP_EOL; - } - if ($properties->getManager() > '') { - $html .= ' ' . PHP_EOL; - } - - if ($pIncludeStyles) { - $html .= $this->generateStyles(true); - } + $html .= ' ' . PHP_EOL; + $html .= ' ' . PHP_EOL; + $html .= ' ' . htmlspecialchars($properties->getTitle()) . '' . PHP_EOL; + $html .= self::generateMeta($properties->getCreator(), 'author'); + $html .= self::generateMeta($properties->getTitle(), 'title'); + $html .= self::generateMeta($properties->getDescription(), 'description'); + $html .= self::generateMeta($properties->getSubject(), 'subject'); + $html .= self::generateMeta($properties->getKeywords(), 'keywords'); + $html .= self::generateMeta($properties->getCategory(), 'category'); + $html .= self::generateMeta($properties->getCompany(), 'company'); + $html .= self::generateMeta($properties->getManager(), 'manager'); + + $html .= $pIncludeStyles ? $this->generateStyles(true) : $this->generatePageDeclarations(true); $html .= ' ' . PHP_EOL; $html .= '' . PHP_EOL; @@ -390,25 +365,61 @@ public function generateHTMLHeader($pIncludeStyles = false) return $html; } - /** - * Generate sheet data. - * - * @return string - */ - public function generateSheetData() + private function generateSheetPrep() { // Ensure that Spans have been calculated? - if ($this->sheetIndex !== null || !$this->spansAreCalculated) { - $this->calculateSpans(); - } + $this->calculateSpans(); // Fetch sheets - $sheets = []; if ($this->sheetIndex === null) { $sheets = $this->spreadsheet->getAllSheets(); } else { - $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex); + $sheets = [$this->spreadsheet->getSheet($this->sheetIndex)]; + } + + return $sheets; + } + + private function generateSheetStarts($sheet, $rowMin) + { + // calculate start of , + $tbodyStart = $rowMin; + $theadStart = $theadEnd = 0; // default: no no + if ($sheet->getPageSetup()->isRowsToRepeatAtTopSet()) { + $rowsToRepeatAtTop = $sheet->getPageSetup()->getRowsToRepeatAtTop(); + + // we can only support repeating rows that start at top row + if ($rowsToRepeatAtTop[0] == 1) { + $theadStart = $rowsToRepeatAtTop[0]; + $theadEnd = $rowsToRepeatAtTop[1]; + $tbodyStart = $rowsToRepeatAtTop[1] + 1; + } + } + + return [$theadStart, $theadEnd, $tbodyStart]; + } + + private function generateSheetTags($row, $theadStart, $theadEnd, $tbodyStart) + { + // ? + $startTag = ($row == $theadStart) ? (' ' . PHP_EOL) : ''; + if (!$startTag) { + $startTag = ($row == $tbodyStart) ? (' ' . PHP_EOL) : ''; } + $endTag = ($row == $theadEnd) ? (' ' . PHP_EOL) : ''; + $cellType = ($row >= $tbodyStart) ? 'td' : 'th'; + + return [$cellType, $startTag, $endTag]; + } + + /** + * Generate sheet data. + * + * @return string + */ + public function generateSheetData() + { + $sheets = $this->generateSheetPrep(); // Construct HTML $html = ''; @@ -418,7 +429,6 @@ public function generateSheetData() foreach ($sheets as $sheet) { // Write table header $html .= $this->generateTableHeader($sheet); - // Get worksheet dimension $dimension = explode(':', $sheet->calculateWorksheetDimension()); $dimension[0] = Coordinate::coordinateFromString($dimension[0]); @@ -430,34 +440,13 @@ public function generateSheetData() $rowMin = $dimension[0][1]; $rowMax = $dimension[1][1]; - // calculate start of , - $tbodyStart = $rowMin; - $theadStart = $theadEnd = 0; // default: no no - if ($sheet->getPageSetup()->isRowsToRepeatAtTopSet()) { - $rowsToRepeatAtTop = $sheet->getPageSetup()->getRowsToRepeatAtTop(); - - // we can only support repeating rows that start at top row - if ($rowsToRepeatAtTop[0] == 1) { - $theadStart = $rowsToRepeatAtTop[0]; - $theadEnd = $rowsToRepeatAtTop[1]; - $tbodyStart = $rowsToRepeatAtTop[1] + 1; - } - } + [$theadStart, $theadEnd, $tbodyStart] = $this->generateSheetStarts($sheet, $rowMin); // Loop through cells $row = $rowMin - 1; while ($row++ < $rowMax) { - // ? - if ($row == $theadStart) { - $html .= ' ' . PHP_EOL; - $cellType = 'th'; - } - - // ? - if ($row == $tbodyStart) { - $html .= ' ' . PHP_EOL; - $cellType = 'td'; - } + [$cellType, $startTag, $endTag] = $this->generateSheetTags($row, $theadStart, $theadEnd, $tbodyStart); + $html .= $startTag; // Write row if there are HTML table cells in it if (!isset($this->isSpannedRow[$sheet->getParent()->getIndex($sheet)][$row])) { @@ -477,23 +466,16 @@ public function generateSheetData() $html .= $this->generateRow($sheet, $rowData, $row - 1, $cellType); } - // ? - if ($row == $theadEnd) { - $html .= ' ' . PHP_EOL; - } + $html .= $endTag; } $html .= $this->extendRowsForChartsAndImages($sheet, $row); - // Close table body. - $html .= ' ' . PHP_EOL; - // Write table footer $html .= $this->generateTableFooter(); - // Writing PDF? - if ($this->isPdf) { + if ($this->isPdf && $this->useInlineCss) { if ($this->sheetIndex === null && $sheetId + 1 < $this->spreadsheet->getSheetCount()) { - $html .= '
'; + $html .= '
'; } } @@ -540,13 +522,30 @@ public function generateNavigation() return $html; } - private function extendRowsForChartsAndImages(Worksheet $pSheet, $row) + /** + * Extend Row if chart is placed after nominal end of row. + * This code should be exercised by sample: + * Chart/32_Chart_read_write_PDF.php. + * However, that test is suppressed due to out-of-date + * Jpgraph code issuing warnings. So, don't measure + * code coverage for this function till that is fixed. + * + * @param Worksheet $pSheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet + * @param int $row Row to check for charts + * + * @return array + * + * @codeCoverageIgnore + */ + private function extendRowsForCharts(Worksheet $pSheet, $row) { $rowMax = $row; $colMax = 'A'; + $anyfound = false; if ($this->includeCharts) { foreach ($pSheet->getChartCollection() as $chart) { if ($chart instanceof Chart) { + $anyfound = true; $chartCoordinates = $chart->getTopLeftPosition(); $chartTL = Coordinate::coordinateFromString($chartCoordinates['cell']); $chartCol = Coordinate::columnIndexFromString($chartTL[0]); @@ -560,44 +559,68 @@ private function extendRowsForChartsAndImages(Worksheet $pSheet, $row) } } + return [$rowMax, $colMax, $anyfound]; + } + + private function extendRowsForChartsAndImages(Worksheet $pSheet, $row) + { + [$rowMax, $colMax, $anyfound] = $this->extendRowsForCharts($pSheet, $row); + foreach ($pSheet->getDrawingCollection() as $drawing) { - if ($drawing instanceof Drawing) { - $imageTL = Coordinate::coordinateFromString($drawing->getCoordinates()); - $imageCol = Coordinate::columnIndexFromString($imageTL[0]); - if ($imageTL[1] > $rowMax) { - $rowMax = $imageTL[1]; - if ($imageCol > Coordinate::columnIndexFromString($colMax)) { - $colMax = $imageTL[0]; - } + $anyfound = true; + $imageTL = Coordinate::coordinateFromString($drawing->getCoordinates()); + $imageCol = Coordinate::columnIndexFromString($imageTL[0]); + if ($imageTL[1] > $rowMax) { + $rowMax = $imageTL[1]; + if ($imageCol > Coordinate::columnIndexFromString($colMax)) { + $colMax = $imageTL[0]; } } } // Don't extend rows if not needed - if ($row === $rowMax) { + if ($row === $rowMax || !$anyfound) { return ''; } $html = ''; ++$colMax; - + ++$row; while ($row <= $rowMax) { $html .= ''; for ($col = 'A'; $col != $colMax; ++$col) { - $html .= ''; - $html .= $this->writeImageInCell($pSheet, $col . $row); - if ($this->includeCharts) { - $html .= $this->writeChartInCell($pSheet, $col . $row); + $htmlx = $this->writeImageInCell($pSheet, $col . $row); + $htmlx .= $this->includeCharts ? $this->writeChartInCell($pSheet, $col . $row) : ''; + if ($htmlx) { + $html .= "$htmlx"; + } else { + $html .= ""; } - $html .= ''; } ++$row; - $html .= ''; + $html .= '' . PHP_EOL; } return $html; } + /** + * Convert Windows file name to file protocol URL. + * + * @param string $filename file name on local system + * + * @return string + */ + public static function winFileToUrl($filename) + { + // Windows filename + if (substr($filename, 1, 2) === ':\\') { + $filename = 'file:///' . str_replace('\\', '/', $filename); + } + + return $filename; + } + /** * Generate image tag in cell. * @@ -613,57 +636,44 @@ private function writeImageInCell(Worksheet $pSheet, $coordinates) // Write images foreach ($pSheet->getDrawingCollection() as $drawing) { + if ($drawing->getCoordinates() != $coordinates) { + continue; + } + $filedesc = $drawing->getDescription(); + $filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded image'; if ($drawing instanceof Drawing) { - if ($drawing->getCoordinates() == $coordinates) { - $filename = $drawing->getPath(); + $filename = $drawing->getPath(); - // Strip off eventual '.' - if (substr($filename, 0, 1) == '.') { - $filename = substr($filename, 1); - } + // Strip off eventual '.' + $filename = preg_replace('/^[.]/', '', $filename); - // Prepend images root - $filename = $this->getImagesRoot() . $filename; + // Prepend images root + $filename = $this->getImagesRoot() . $filename; - // Strip off eventual '.' - if (substr($filename, 0, 1) == '.' && substr($filename, 0, 2) != './') { - $filename = substr($filename, 1); - } + // Strip off eventual '.' if followed by non-/ + $filename = preg_replace('@^[.]([^/])@', '$1', $filename); - // Convert UTF8 data to PCDATA - $filename = htmlspecialchars($filename); + // Convert UTF8 data to PCDATA + $filename = htmlspecialchars($filename); - $html .= PHP_EOL; - if ((!$this->embedImages) || ($this->isPdf)) { - $imageData = $filename; - } else { + $html .= PHP_EOL; + $imageData = self::winFileToUrl($filename); + + if ($this->embedImages && !$this->isPdf) { + $picture = @file_get_contents($filename); + if ($picture !== false) { $imageDetails = getimagesize($filename); - if ($fp = fopen($filename, 'rb', 0)) { - $picture = ''; - while (!feof($fp)) { - $picture .= fread($fp, 1024); - } - fclose($fp); - // base64 encode the binary data, then break it - // into chunks according to RFC 2045 semantics - $base64 = chunk_split(base64_encode($picture)); - $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64; - } else { - $imageData = $filename; - } + // base64 encode the binary data + $base64 = base64_encode($picture); + $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64; } - - $html .= '
'; - $html .= ''; - $html .= '
'; } + + $html .= '' . $filedesc . ''; } elseif ($drawing instanceof MemoryDrawing) { - if ($drawing->getCoordinates() != $coordinates) { - continue; - } ob_start(); // Let's start output buffering. imagepng($drawing->getImageResource()); // This will normally output the image, but because of ob_start(), it won't. $contents = ob_get_contents(); // Instead, output above is saved to $contents @@ -675,7 +685,7 @@ private function writeImageInCell(Worksheet $pSheet, $coordinates) // max-width: 100% ensures that image doesnt overflow containing cell // width: X sets width of supplied image. // As a result, images bigger than cell will be contained and images smaller will not get stretched - $html .= ''; + $html .= '' . $filedesc . ''; } } @@ -684,11 +694,18 @@ private function writeImageInCell(Worksheet $pSheet, $coordinates) /** * Generate chart tag in cell. + * This code should be exercised by sample: + * Chart/32_Chart_read_write_PDF.php. + * However, that test is suppressed due to out-of-date + * Jpgraph code issuing warnings. So, don't measure + * code coverage for this function till that is fixed. * * @param Worksheet $pSheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet * @param string $coordinates Cell coordinates * * @return string + * + * @codeCoverageIgnore */ private function writeChartInCell(Worksheet $pSheet, $coordinates) { @@ -707,17 +724,17 @@ private function writeChartInCell(Worksheet $pSheet, $coordinates) $html .= PHP_EOL; $imageDetails = getimagesize($chartFileName); + $filedesc = $chart->getTitle(); + $filedesc = $filedesc ? self::getChartCaption($filedesc->getCaption()) : ''; + $filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded chart'; if ($fp = fopen($chartFileName, 'rb', 0)) { $picture = fread($fp, filesize($chartFileName)); fclose($fp); - // base64 encode the binary data, then break it - // into chunks according to RFC 2045 semantics - $base64 = chunk_split(base64_encode($picture)); + // base64 encode the binary data + $base64 = base64_encode($picture); $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64; - $html .= '
'; - $html .= '' . PHP_EOL; - $html .= '
'; + $html .= '' . $filedesc . '' . PHP_EOL; unlink($chartFileName); } @@ -729,6 +746,27 @@ private function writeChartInCell(Worksheet $pSheet, $coordinates) return $html; } + /** + * Extend Row if chart is placed after nominal end of row. + * This code should be exercised by sample: + * Chart/32_Chart_read_write_PDF.php. + * However, that test is suppressed due to out-of-date + * Jpgraph code issuing warnings. So, don't measure + * code coverage for this function till that is fixed. + * Caption is described in documentation as fixed, + * but in 32_Chart it is somehow an array of RichText. + * + * @param mixed $cap + * + * @return string + * + * @codeCoverageIgnore + */ + private static function getChartCaption($cap) + { + return is_array($cap) ? implode(' ', $cap) : $cap; + } + /** * Generate CSS styles. * @@ -747,7 +785,7 @@ public function generateStyles($generateSurroundingHTML = true) // Start styles if ($generateSurroundingHTML) { $html .= ' \n"; + // Identify which rows should be omitted in HTML. These are the rows where all the cells + // participate in a merge and the where base cells are somewhere above. + $countColumns = Coordinate::columnIndexFromString($sheet->getHighestColumn()); + foreach ($candidateSpannedRow as $rowIndex) { + if (isset($this->isSpannedCell[$sheetIndex][$rowIndex])) { + if (count($this->isSpannedCell[$sheetIndex][$rowIndex]) == $countColumns) { + $this->isSpannedRow[$sheetIndex][$rowIndex] = $rowIndex; + } + } + } + + // For each of the omitted rows we found above, the affected rowspans should be subtracted by 1 + if (isset($this->isSpannedRow[$sheetIndex])) { + foreach ($this->isSpannedRow[$sheetIndex] as $rowIndex) { + $adjustedBaseCells = []; + $c = -1; + $e = $countColumns - 1; + while ($c++ < $e) { + $baseCell = $this->isSpannedCell[$sheetIndex][$rowIndex][$c]['baseCell']; + + if (!in_array($baseCell, $adjustedBaseCells)) { + // subtract rowspan by 1 + --$this->isBaseCell[$sheetIndex][$baseCell[0]][$baseCell[1]]['rowspan']; + $adjustedBaseCells[] = $baseCell; + } + } + } + } } /** @@ -1668,4 +1788,70 @@ private function writeComment(Worksheet $pSheet, $coordinate) return $result; } + + /** + * Generate @page declarations. + * + * @param bool $generateSurroundingHTML + * + * @return string + */ + private function generatePageDeclarations($generateSurroundingHTML) + { + // Ensure that Spans have been calculated? + $this->calculateSpans(); + + // Fetch sheets + $sheets = []; + if ($this->sheetIndex === null) { + $sheets = $this->spreadsheet->getAllSheets(); + } else { + $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex); + } + + // Construct HTML + $htmlPage = $generateSurroundingHTML ? ('' . PHP_EOL) : ''; + + return $htmlPage; + } } diff --git a/src/PhpSpreadsheet/Writer/Pdf.php b/src/PhpSpreadsheet/Writer/Pdf.php index bb9f4aefd9..872204589f 100644 --- a/src/PhpSpreadsheet/Writer/Pdf.php +++ b/src/PhpSpreadsheet/Writer/Pdf.php @@ -2,7 +2,6 @@ namespace PhpOffice\PhpSpreadsheet\Writer; -use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Shared\File; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; @@ -38,13 +37,6 @@ abstract class Pdf extends Html */ protected $paperSize; - /** - * Temporary storage for Save Array Return type. - * - * @var string - */ - private $saveArrayReturnType; - /** * Paper Sizes xRef List. * @@ -127,8 +119,9 @@ abstract class Pdf extends Html public function __construct(Spreadsheet $spreadsheet) { parent::__construct($spreadsheet); - $this->setUseInlineCss(true); - $this->tempDir = File::sysGetTempDir(); + //$this->setUseInlineCss(true); + $this->tempDir = File::sysGetTempDir() . '/phpsppdf'; + $this->isPdf = true; } /** @@ -244,20 +237,9 @@ public function setTempDir($pValue) */ protected function prepareForSave($pFilename) { - // garbage collect - $this->spreadsheet->garbageCollect(); - - $this->saveArrayReturnType = Calculation::getArrayReturnType(); - Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_VALUE); - // Open file $this->openFileHandle($pFilename); - // Set PDF - $this->isPdf = true; - // Build CSS - $this->buildCSS(true); - return $this->fileHandle; } @@ -267,7 +249,5 @@ protected function prepareForSave($pFilename) protected function restoreStateAfterSave(): void { $this->maybeCloseFileHandle(); - - Calculation::setArrayReturnType($this->saveArrayReturnType); } } diff --git a/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php b/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php index 4506468bb4..3ecaff682d 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php +++ b/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php @@ -61,11 +61,7 @@ public function save($pFilename) $pdf = $this->createExternalWriterInstance(); $pdf->setPaper(strtolower($paperSize), $orientation); - $pdf->loadHtml( - $this->generateHTMLHeader(false) . - $this->generateSheetData() . - $this->generateHTMLFooter() - ); + $pdf->loadHtml($this->generateHTMLAll()); $pdf->render(); // Write to file diff --git a/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php b/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php index 6f3cc57f68..4424157555 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php +++ b/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php @@ -81,12 +81,10 @@ public function save($pFilename) $pdf->SetKeywords($this->spreadsheet->getProperties()->getKeywords()); $pdf->SetCreator($this->spreadsheet->getProperties()->getCreator()); - $pdf->WriteHTML($this->generateHTMLHeader(false)); - $html = $this->generateSheetData(); + $html = $this->generateHTMLAll(); foreach (\array_chunk(\explode(PHP_EOL, $html), 1000) as $lines) { $pdf->WriteHTML(\implode(PHP_EOL, $lines)); } - $pdf->WriteHTML($this->generateHTMLFooter()); // Write to file fwrite($fileHandle, $pdf->Output('', 'S')); diff --git a/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php b/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php index 5c31af8f8b..3dc172a2e2 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php +++ b/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php @@ -2,11 +2,23 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Pdf; +use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; use PhpOffice\PhpSpreadsheet\Writer\Pdf; class Tcpdf extends Pdf { + /** + * Create a new PDF Writer instance. + * + * @param Spreadsheet $spreadsheet Spreadsheet object + */ + public function __construct(Spreadsheet $spreadsheet) + { + parent::__construct($spreadsheet); + $this->setUseInlineCss(true); + } + /** * Gets the implementation of external PDF library that should be used. * @@ -75,11 +87,7 @@ public function save($pFilename) // Set the appropriate font $pdf->SetFont($this->getFont()); - $pdf->writeHTML( - $this->generateHTMLHeader(false) . - $this->generateSheetData() . - $this->generateHTMLFooter() - ); + $pdf->writeHTML($this->generateHTMLAll()); // Document info $pdf->SetTitle($this->spreadsheet->getProperties()->getTitle()); diff --git a/tests/PhpSpreadsheetTests/Style/NumberFormatTest.php b/tests/PhpSpreadsheetTests/Style/NumberFormatTest.php index 9dc20fd72a..ba44b6afe8 100644 --- a/tests/PhpSpreadsheetTests/Style/NumberFormatTest.php +++ b/tests/PhpSpreadsheetTests/Style/NumberFormatTest.php @@ -45,4 +45,18 @@ public function providerNumberFormatDates() { return require 'tests/data/Style/NumberFormatDates.php'; } + + public function testCurrencyCode() + { + // "Currency symbol" replaces $ in some cases, not in others + $cur = StringHelper::getCurrencyCode(); + StringHelper::setCurrencyCode('€'); + $fmt1 = '#,##0.000\ [$]'; + $rslt = NumberFormat::toFormattedString(12345.679, $fmt1); + self::assertEquals($rslt, '12,345.679 €'); + $fmt2 = '$ #,##0.000'; + $rslt = NumberFormat::toFormattedString(12345.679, $fmt2); + self::assertEquals($rslt, '$ 12,345.679'); + StringHelper::setCurrencyCode($cur); + } } diff --git a/tests/PhpSpreadsheetTests/Writer/Html/AllOrOneSheetTest.php b/tests/PhpSpreadsheetTests/Writer/Html/AllOrOneSheetTest.php new file mode 100644 index 0000000000..03fe741f30 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/AllOrOneSheetTest.php @@ -0,0 +1,192 @@ +getActiveSheet(); + $sheet1->setCellValue('A1', 'first'); + $sheet2 = $spreadsheet->createSheet(); + $sheet2->setCellValue('A1', 'second'); + + $writer = new Html($spreadsheet); + self::assertFalse($writer->getEmbedImages()); + $writer->writeAllSheets(); + self::assertTrue($writer->getGenerateSheetNavigationBlock()); + $html = $writer->generateHTMLAll(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('ul'); // sheet navigation + self::assertCount(1, $divs); + $divs = $body->getElementsByTagName('div'); + self::assertCount(2, $divs); + self::assertEquals('page: page0', $divs->item(0)->getAttribute('style')); + $tbl = $divs->item(0)->getElementsByTagName('table'); + self::assertEquals('sheet0', $tbl->item(0)->getAttribute('id')); + self::assertEquals('sheet0 gridlines', $tbl->item(0)->getAttribute('class')); + $tbl = $divs->item(1)->getElementsByTagName('table'); + self::assertEquals('page: page1', $divs->item(1)->getAttribute('style')); + self::assertEquals('sheet1', $tbl->item(0)->getAttribute('id')); + self::assertEquals('sheet1 gridlines', $tbl->item(0)->getAttribute('class')); + $this->writeAndReload($spreadsheet, 'Html'); + } + + public function testWriteAllSheetsNoNav() + { + $spreadsheet = new Spreadsheet(); + $sheet1 = $spreadsheet->getActiveSheet(); + $sheet1->setCellValue('A1', 'first'); + $sheet2 = $spreadsheet->createSheet(); + $sheet2->setCellValue('A1', 'second'); + + $writer = new Html($spreadsheet); + $writer->writeAllSheets(); + $writer->setGenerateSheetNavigationBlock(false); + $html = $writer->generateHTMLAll(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('ul'); // sheet navigation + self::assertCount(0, $divs); + $divs = $body->getElementsByTagName('div'); + self::assertCount(2, $divs); + self::assertEquals('page: page0', $divs->item(0)->getAttribute('style')); + $tbl = $divs->item(0)->getElementsByTagName('table'); + self::assertEquals('sheet0', $tbl->item(0)->getAttribute('id')); + self::assertEquals('sheet0 gridlines', $tbl->item(0)->getAttribute('class')); + $tbl = $divs->item(1)->getElementsByTagName('table'); + self::assertEquals('page: page1', $divs->item(1)->getAttribute('style')); + self::assertEquals('sheet1', $tbl->item(0)->getAttribute('id')); + self::assertEquals('sheet1 gridlines', $tbl->item(0)->getAttribute('class')); + $this->writeAndReload($spreadsheet, 'Html'); + } + + public function testWriteAllSheetsPdf() + { + $spreadsheet = new Spreadsheet(); + $sheet1 = $spreadsheet->getActiveSheet(); + $sheet1->setCellValue('A1', 'first'); + $sheet2 = $spreadsheet->createSheet(); + $sheet2->setCellValue('A1', 'second'); + + $writer = new Mpdf($spreadsheet); + $writer->writeAllSheets(); + $html = $writer->generateHTMLAll(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('ul'); // sheet navigation + self::assertCount(0, $divs); + $divs = $body->getElementsByTagName('div'); + self::assertCount(2, $divs); + self::assertEquals('page: page0', $divs->item(0)->getAttribute('style')); + $tbl = $divs->item(0)->getElementsByTagName('table'); + self::assertEquals('sheet0', $tbl->item(0)->getAttribute('id')); + self::assertEquals('sheet0 gridlines', $tbl->item(0)->getAttribute('class')); + $tbl = $divs->item(1)->getElementsByTagName('table'); + self::assertEquals('page: page1', $divs->item(1)->getAttribute('style')); + self::assertEquals('sheet1', $tbl->item(0)->getAttribute('id')); + self::assertEquals('sheet1 gridlines', $tbl->item(0)->getAttribute('class')); + } + + public function testWriteOneSheet() + { + $spreadsheet = new Spreadsheet(); + $sheet1 = $spreadsheet->getActiveSheet(); + $sheet1->setCellValue('A1', 'first'); + $sheet2 = $spreadsheet->createSheet(); + $sheet2->setCellValue('A1', 'second'); + + $writer = new Html($spreadsheet); + $writer->setSheetIndex(1); + $html = $writer->generateHTMLAll(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('ul'); // sheet navigation + self::assertCount(0, $divs); + $divs = $body->getElementsByTagName('div'); + self::assertCount(1, $divs); + self::assertEquals('page: page1', $divs->item(0)->getAttribute('style')); + $tbl = $divs->item(0)->getElementsByTagName('table'); + self::assertEquals('sheet1', $tbl->item(0)->getAttribute('id')); + self::assertEquals('sheet1 gridlines', $tbl->item(0)->getAttribute('class')); + $this->writeAndReload($spreadsheet, 'Html'); + } + + public function testPageBreak() + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setShowGridlines(true)->setPrintGridlines(true); + $sheet->setCellValue('A1', 1); + $sheet->setCellValue('A2', 'before page break'); + $sheet->setBreak('A2', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW); + $sheet->setCellValue('A3', 'after page break'); + $sheet->setCellValue('A4', 4); + $sheet = $spreadsheet->createSheet(); + $sheet->setCellValue('A1', 'new sheet'); + + $writer = new Html($spreadsheet); + $writer->writeAllSheets(); + + $html = $writer->generateHTMLAll(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('div'); + self::assertCount(3, $divs); + + $sty = $divs[0]->getAttribute('style'); + $cls = $divs[0]->getAttribute('class'); + self::assertEquals('page: page0', $sty); + self::assertEquals('', $cls); + $sty = $divs[1]->getAttribute('style'); + $cls = $divs[1]->getAttribute('class'); + self::assertEquals('page: page0', $sty); + self::assertEquals('scrpgbrk', $cls); + $sty = $divs[2]->getAttribute('style'); + $cls = $divs[2]->getAttribute('class'); + self::assertEquals('page: page1', $sty); + self::assertEquals('', $cls); + + $this->writeAndReload($spreadsheet, 'Html'); + } + + public function testTcpdfPageBreak() + { + $spreadsheet = new Spreadsheet(); + $sheet1 = $spreadsheet->getActiveSheet(); + $sheet1->setCellValue('A1', 'First sheet'); + $sheet2 = $spreadsheet->createSheet(); + $sheet2->setCellValue('A2', 'Second sheet'); + $sheet2->setCellValue('A2', 'before page break'); + $sheet2->setBreak('A2', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW); + $sheet2->setCellValue('A3', 'after page break'); + + $writer = new Tcpdf($spreadsheet); + $writer->writeAllSheets(); + $html = $writer->generateHtmlAll(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('div'); + self::assertCount(5, $divs); + + self::assertEquals('page: page0', $divs[0]->getAttribute('style')); + self::assertEquals('page: page1', $divs[2]->getAttribute('style')); + self::assertEquals('page: page1', $divs[4]->getAttribute('style')); + self::assertEquals('page-break-before:always', $divs[1]->getAttribute('style')); + self::assertEquals('page-break-before:always', $divs[3]->getAttribute('style')); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Html/GridlinesTest.php b/tests/PhpSpreadsheetTests/Writer/Html/GridlinesTest.php new file mode 100644 index 0000000000..2bbe3541fb --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/GridlinesTest.php @@ -0,0 +1,199 @@ +getActiveSheet(); + $sheet->setShowGridlines(true)->setPrintGridlines(true); + $sheet->setCellValue('A1', 1); + $sheet = $spreadsheet->createSheet(); + $sheet->setShowGridlines(true)->setPrintGridlines(false); + $sheet->setCellValue('A1', 1); + $sheet = $spreadsheet->createSheet(); + $sheet->setShowGridlines(false)->setPrintGridlines(true); + $sheet->setCellValue('A1', 1); + $sheet = $spreadsheet->createSheet(); + $sheet->setShowGridlines(false)->setPrintGridlines(false); + $sheet->setCellValue('A1', 1); + + $writer = new Html($spreadsheet); + $writer->writeAllSheets(); + + $html = $writer->generateHTMLAll(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('div'); + self::assertCount(4, $divs); + + $tbl = $divs[0]->getElementsByTagName('table')[0]; + $cls = $tbl->getAttribute('class'); + self::assertEquals('sheet0 gridlines gridlinesp', $cls); + $tbl = $divs[1]->getElementsByTagName('table')[0]; + $cls = $tbl->getAttribute('class'); + self::assertEquals('sheet1 gridlines', $cls); + $tbl = $divs[2]->getElementsByTagName('table')[0]; + $cls = $tbl->getAttribute('class'); + self::assertEquals('sheet2 gridlinesp', $cls); + $tbl = $divs[3]->getElementsByTagName('table')[0]; + $cls = $tbl->getAttribute('class'); + self::assertEquals('sheet3', $cls); + + $this->writeAndReload($spreadsheet, 'Html'); + } + + public function testGridlinesInline() + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setShowGridlines(true)->setPrintGridlines(true); + $sheet->setCellValue('A1', 1); + $sheet = $spreadsheet->createSheet(); + $sheet->setShowGridlines(true)->setPrintGridlines(false); + $sheet->setCellValue('A1', 1); + $sheet = $spreadsheet->createSheet(); + $sheet->setShowGridlines(false)->setPrintGridlines(true); + $sheet->setCellValue('A1', 1); + $sheet = $spreadsheet->createSheet(); + $sheet->setShowGridlines(false)->setPrintGridlines(false); + $sheet->setCellValue('A1', 1); + + $writer = new Html($spreadsheet); + $writer->writeAllSheets(); + $writer->setUseInlineCss(true); + + $html = $writer->generateHTMLAll(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('div'); + self::assertCount(4, $divs); + + $tbl = $divs[0]->getElementsByTagName('table')[0]; + $cls = $tbl->getAttribute('class'); + self::assertEquals('gridlines gridlinesp', $cls); + $tbl = $divs[1]->getElementsByTagName('table')[0]; + $cls = $tbl->getAttribute('class'); + self::assertEquals('gridlines', $cls); + $tbl = $divs[2]->getElementsByTagName('table')[0]; + $cls = $tbl->getAttribute('class'); + self::assertEquals('gridlinesp', $cls); + $tbl = $divs[3]->getElementsByTagName('table')[0]; + $cls = $tbl->getAttribute('class'); + self::assertEquals('', $cls); + + $this->writeAndReload($spreadsheet, 'Html'); + } + + public function testRichText() + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $emc2 = new \PhpOffice\PhpSpreadsheet\RichText\RichText(); + $part1 = $emc2->createTextRun('e=mc'); + $part1->getFont()->getColor()->setARGB(Color::COLOR_BLUE); + $part2 = $emc2->createTextRun('2'); + $font = $part2->getFont(); + $font->getColor()->setARGB(Color::COLOR_DARKGREEN); + $font->setSuperScript(true); + $sheet->setCellValue('A1', $emc2); + $h2o = new \PhpOffice\PhpSpreadsheet\RichText\RichText(); + $h2o->createTextRun('H'); + $part2 = $h2o->createTextRun('2'); + $font = $part2->getFont(); + $font->setSubScript(true); + $font->getColor()->setARGB(Color::COLOR_RED); + $h2o->createTextRun('O'); + $sheet->setCellValue('A2', $h2o); + $h2so4 = new \PhpOffice\PhpSpreadsheet\RichText\RichText(); + $h2so4->createTextRun('H'); + $part2 = $h2so4->createTextRun('2'); + $font = $part2->getFont(); + $font->setSubScript(true); + $h2so4->createTextRun('SO'); + $part4 = $h2so4->createTextRun('4'); + $part4->getFont()->setSubScript(true); + $sheet->setCellValue('A3', $h2so4); + $sheet->setCellValue('A4', '5'); + $sheet->getCell('A4')->getStyle()->getFont()->setSuperScript(true); + $sheet->setCellValue('A5', '6'); + $sheet->getCell('A5')->getStyle()->getFont()->setSubScript(true); + + $writer = new Html($spreadsheet); + $html = $writer->generateHTMLAll(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('div'); + self::assertCount(1, $divs); + + $tabl = $divs[0]->getElementsByTagName('table'); + $tbod = $tabl[0]->getElementsByTagName('tbody'); + $rows = $tbod[0]->getElementsByTagName('tr'); + self::assertCount(5, $rows); + $tds = $rows[0]->getElementsByTagName('td'); + self::assertCount(1, $tds); + $spans = $tds[0]->getElementsByTagName('span'); + self::assertCount(2, $spans); + self::assertEquals('e=mc', $spans[0]->textContent); + $style = $spans[0]->getAttribute('style'); + self::assertEquals(1, preg_match('/color:#0000FF/', $style)); + $style = $spans[1]->getAttribute('style'); + self::assertEquals(1, preg_match('/color:#008000/', $style)); + $sups = $spans[1]->getElementsByTagName('sup'); + self::assertCount(1, $sups); + assert('2' == $sups[0]->textContent); + + $tds = $rows[1]->getElementsByTagName('td'); + assert(1 == count($tds)); + $spans = $tds[0]->getElementsByTagName('span'); + assert(3 == count($spans)); + assert('H' == $spans[0]->textContent); + $style = $spans[1]->getAttribute('style'); + assert(1 == preg_match('/color:#FF0000/', $style)); + $subs = $spans[1]->getElementsByTagName('sub'); + assert(1 == count($subs)); + assert('2' == $subs[0]->textContent); + assert('O' == $spans[2]->textContent); + + $tds = $rows[2]->getElementsByTagName('td'); + self::assertCount(1, $tds); + $spans = $tds[0]->getElementsByTagName('span'); + self::assertCount(4, $spans); + self::assertEquals('H', $spans[0]->textContent); + $subs = $spans[1]->getElementsByTagName('sub'); + self::assertCount(1, $subs); + self::assertEquals('2', $subs[0]->textContent); + self::assertEquals('SO', $spans[2]->textContent); + $subs = $spans[3]->getElementsByTagName('sub'); + self::assertCount(1, $subs); + self::assertEquals('4', $subs[0]->textContent); + + $tds = $rows[3]->getElementsByTagName('td'); + self::assertCount(1, $tds); + $spans = $tds[0]->getElementsByTagName('span'); + self::assertCount(0, $spans); + $sups = $tds[0]->getElementsByTagName('sup'); + self::assertCount(1, $sups); + self::assertEquals('5', $sups[0]->textContent); + + $tds = $rows[4]->getElementsByTagName('td'); + self::assertCount(1, $tds); + $spans = $tds[0]->getElementsByTagName('span'); + self::assertCount(0, $spans); + $subs = $tds[0]->getElementsByTagName('sub'); + self::assertCount(1, $subs); + self::assertEquals('6', $subs[0]->textContent); + + $this->writeAndReload($spreadsheet, 'Html'); + } +} diff --git a/tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php b/tests/PhpSpreadsheetTests/Writer/Html/HtmlCommentsTest.php similarity index 92% rename from tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php rename to tests/PhpSpreadsheetTests/Writer/Html/HtmlCommentsTest.php index 1b497277a6..637c6514cb 100644 --- a/tests/PhpSpreadsheetTests/Functional/HtmlCommentsTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Html/HtmlCommentsTest.php @@ -1,11 +1,12 @@ decsep = StringHelper::getDecimalSeparator(); + StringHelper::setDecimalSeparator('.'); + $this->thosep = StringHelper::getThousandsSeparator(); + StringHelper::setThousandsSeparator(','); + } + + protected function tearDown(): void + { + StringHelper::setDecimalSeparator($this->decsep); + StringHelper::setThousandsSeparator($this->thosep); + } + + public function testColorNumberFormat() + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setCellValue('A1', -50); + $sheet->setCellValue('A2', 3000); + $sheet->setCellValue('A3', 0); + $sheet->setCellValue('A4', '
'); + $fmt = '[Blue]$#,##0;[Red]$#,##0;$#,##0'; + $sheet->getStyle('A1:A4')->getNumberFormat()->setFormatCode($fmt); + + $writer = new Html($spreadsheet); + $html = $writer->generateHTMLAll(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('div'); + + $tabl = $divs[0]->getElementsByTagName('table'); + $tbod = $tabl[0]->getElementsByTagName('tbody'); + $rows = $tbod[0]->getElementsByTagName('tr'); + self::assertCount(4, $rows); + + $tds = $rows[0]->getElementsByTagName('td'); + self::assertCount(1, $tds); + $spans = $tds[0]->getElementsByTagName('span'); + self::assertCount(1, $spans); + $style = $spans[0]->getAttribute('style'); + self::assertEquals(1, preg_match('/color:red/', $style)); + self::assertEquals('$50', $spans[0]->textContent); + + $tds = $rows[1]->getElementsByTagName('td'); + self::assertCount(1, $tds); + $spans = $tds[0]->getElementsByTagName('span'); + self::assertCount(1, $spans); + $style = $spans[0]->getAttribute('style'); + self::assertEquals(1, preg_match('/color:blue/', $style)); + self::assertEquals('$3,000', $spans[0]->textContent); + + $tds = $rows[2]->getElementsByTagName('td'); + self::assertCount(1, $tds); + $spans = $tds[0]->getElementsByTagName('span'); + self::assertCount(0, $spans); + self::assertEquals('$0', $tds[0]->textContent); + + $tds = $rows[3]->getElementsByTagName('td'); + self::assertCount(1, $tds); + $spans = $tds[0]->getElementsByTagName('span'); + self::assertCount(0, $spans); + self::assertEquals('
', $tds[0]->textContent); + + $this->writeAndReload($spreadsheet, 'Html'); + } + + public function testColorNumberFormatComplex() + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setCellValue('A1', -50); + $sheet->setCellValue('A2', 3000.75); + $sheet->setCellValue('A3', 0); + $sheet->setCellValue('A4', 3000.25); + $fmt = '[Blue][>=3000.5]$#,##0.00;[Red][<0]$#,##0.00;$#,##0.00'; + $sheet->getStyle('A1:A4')->getNumberFormat()->setFormatCode($fmt); + + $writer = new Html($spreadsheet); + $html = $writer->generateHTMLAll(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('div'); + + $tabl = $divs[0]->getElementsByTagName('table'); + $tbod = $tabl[0]->getElementsByTagName('tbody'); + $rows = $tbod[0]->getElementsByTagName('tr'); + self::assertCount(4, $rows); + + $tds = $rows[0]->getElementsByTagName('td'); + self::assertCount(1, $tds); + $spans = $tds[0]->getElementsByTagName('span'); + self::assertCount(1, $spans); + $style = $spans[0]->getAttribute('style'); + self::assertEquals(1, preg_match('/color:red/', $style)); + self::assertEquals('$50.00', $spans[0]->textContent); + + $tds = $rows[1]->getElementsByTagName('td'); + self::assertCount(1, $tds); + $spans = $tds[0]->getElementsByTagName('span'); + self::assertCount(1, $spans); + $style = $spans[0]->getAttribute('style'); + self::assertEquals(1, preg_match('/color:blue/', $style)); + self::assertEquals('$3,000.75', $spans[0]->textContent); + + $tds = $rows[2]->getElementsByTagName('td'); + self::assertCount(1, $tds); + $spans = $tds[0]->getElementsByTagName('span'); + self::assertCount(0, $spans); + self::assertEquals('$0.00', $tds[0]->textContent); + + $tds = $rows[3]->getElementsByTagName('td'); + self::assertCount(1, $tds); + $spans = $tds[0]->getElementsByTagName('span'); + self::assertCount(0, $spans); + self::assertEquals('$3,000.25', $tds[0]->textContent); + + $this->writeAndReload($spreadsheet, 'Html'); + } + + /** + * @dataProvider providerNumberFormat + * + * @param mixed $expectedResult + * @param mixed $val + * @param mixed $fmt + */ + public function testFormatValueWithMask($expectedResult, $val, $fmt) + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->getCell('A1')->setValue($val)->getStyle()->getNumberFormat()->setFormatCode($fmt); + + $writer = new Html($spreadsheet); + $html = $writer->generateHTMLAll(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('div'); + + $tabl = $divs[0]->getElementsByTagName('table'); + $tbod = $tabl[0]->getElementsByTagName('tbody'); + $rows = $tbod[0]->getElementsByTagName('tr'); + + $tds = $rows[0]->getElementsByTagName('td'); + $nbsp = html_entity_decode(' '); + self::assertEquals($expectedResult, str_replace($nbsp, ' ', $tds[0]->textContent)); + + $this->writeAndReload($spreadsheet, 'Html'); + } + + public function providerNumberFormat() + { + return require __DIR__ . '/../../../data/Style/NumberFormat.php'; + } + + /** + * @dataProvider providerNumberFormatDates + * + * @param mixed $expectedResult + * @param mixed $val + * @param mixed $fmt + */ + public function testFormatValueWithMaskDate($expectedResult, $val, $fmt) + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->getCell('A1')->setValue($val)->getStyle()->getNumberFormat()->setFormatCode($fmt); + + $writer = new Html($spreadsheet); + $html = $writer->generateHTMLAll(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('div'); + + $tabl = $divs[0]->getElementsByTagName('table'); + $tbod = $tabl[0]->getElementsByTagName('tbody'); + $rows = $tbod[0]->getElementsByTagName('tr'); + + $tds = $rows[0]->getElementsByTagName('td'); + $nbsp = html_entity_decode(' '); + self::assertEquals($expectedResult, str_replace($nbsp, ' ', $tds[0]->textContent)); + + $this->writeAndReload($spreadsheet, 'Html'); + } + + public function providerNumberFormatDates() + { + return require __DIR__ . '/../../../data/Style/NumberFormatDates.php'; + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Html/ImagesRootTest.php b/tests/PhpSpreadsheetTests/Writer/Html/ImagesRootTest.php new file mode 100644 index 0000000000..f7716e33c2 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/ImagesRootTest.php @@ -0,0 +1,54 @@ +getActiveSheet(); + $drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing(); + $drawing->setName('Test'); + $drawing->setDescription('Test'); + $root = 'http://www.example.com'; + $newdir = __DIR__ . '/../../../data/Reader/HTML'; + $stub = 'image.jpg'; + $imagePath = "./$stub"; + $curdir = getcwd(); + chdir($newdir); + self::assertFileExists($imagePath); + $drawing->setPath($imagePath); + $desc = 'Test tag'; + $drawing->setDescription($desc); + $drawing->setHeight(36); + $drawing->setWorksheet($spreadsheet->getActiveSheet()); + $drawing->setCoordinates('A1'); + $sheet->setCellValue('A2', 'Image Above?'); + + $writer = new Html($spreadsheet); + $writer->setImagesRoot($root); + $html = $writer->generateHTMLAll(); + chdir($curdir); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('div'); + + $tabl = $divs[0]->getElementsByTagName('table'); + $tbod = $tabl[0]->getElementsByTagName('tbody'); + $rows = $tbod[0]->getElementsByTagName('tr'); + self::assertCount(2, $rows); + + $tds = $rows[0]->getElementsByTagName('td'); + self::assertCount(1, $tds); + $img = $tds[0]->getElementsByTagName('img'); + self::assertCount(1, $img); + self::assertEquals("$root/$stub", $img[0]->getAttribute('src')); + self::assertEquals($desc, $img[0]->getAttribute('alt')); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Html/InvalidFileNameTest.php b/tests/PhpSpreadsheetTests/Writer/Html/InvalidFileNameTest.php new file mode 100644 index 0000000000..f536d2bdbd --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/InvalidFileNameTest.php @@ -0,0 +1,53 @@ +expectException(WriterException::class); + $spreadsheet = new Spreadsheet(); + $spreadsheet->getActiveSheet()->getCell('A1')->setValue('Cell 1'); + $writer = new Html($spreadsheet); + $writer->save(''); + } + + public function testEmptyFileNamePdf() + { + $this->expectException(WriterException::class); + $spreadsheet = new Spreadsheet(); + $spreadsheet->getActiveSheet()->getCell('A1')->setValue('Cell 1'); + $writer = new Mpdf($spreadsheet); + $writer->save(''); + } + + public function testEmptyTempdirNamePdf() + { + $this->expectException(WriterException::class); + $spreadsheet = new Spreadsheet(); + $spreadsheet->getActiveSheet()->getCell('A1')->setValue('Cell 1'); + $writer = new Mpdf($spreadsheet); + $writer->setFont('Helvetica'); + self::assertEquals('Helvetica', $writer->getFont()); + $writer->setPaperSize(PageSetup::PAPERSIZE_LEDGER); + self::assertEquals($writer->getPaperSize(), PageSetup::PAPERSIZE_LEDGER); + self::assertEquals(File::sysGetTempDir() . '/phpsppdf', $writer->getTempDir()); + $writer->setTempDir(''); + } + + public function testWinFileNames() + { + self::assertEquals('file:///C:/temp/filename.xlsx', Html::winFileToUrl('C:\\temp\filename.xlsx')); + self::assertEquals('/tmp/filename.xlsx', Html::winFileToUrl('/tmp/filename.xlsx')); + self::assertEquals('a:bfile', Html::winFileToUrl('a:bfile')); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Html/RepeatedRowsTest.php b/tests/PhpSpreadsheetTests/Writer/Html/RepeatedRowsTest.php new file mode 100644 index 0000000000..e38e4b8e9a --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/RepeatedRowsTest.php @@ -0,0 +1,104 @@ +getActiveSheet(); + $sheet->getPageSetup()->setRowsToRepeatAtTop([1, 2]); + $sheet->setCellValue('A1', 'Repeat1'); + $sheet->setCellValue('A2', 'Repeat2'); + for ($row = 3; $row <= 100; ++$row) { + $sheet->setCellValue("A$row", $row); + } + + $writer = new Html($spreadsheet); + $html = $writer->generateHTMLall(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('div'); + $tbl = $divs->item(0)->getElementsByTagName('table'); + self::assertEquals('', $tbl->item(0)->getAttribute('style')); + $thd = $divs->item(0)->getElementsByTagName('thead'); + self::assertCount(1, $thd); + $trw = $thd->item(0)->getElementsByTagName('tr'); + self::assertCount(2, $trw); + $tbd = $divs->item(0)->getElementsByTagName('tbody'); + self::assertCount(1, $tbd); + $trw = $tbd->item(0)->getElementsByTagName('tr'); + self::assertCount(98, $trw); + + $this->writeAndReload($spreadsheet, 'Html'); + } + + public function testWriteNoRepeats() + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + //$sheet->getPageSetup()->setRowsToRepeatAtTop([1, 2]); + $sheet->setCellValue('A1', 'Repeat1'); + $sheet->setCellValue('A2', 'Repeat2'); + for ($row = 3; $row <= 100; ++$row) { + $sheet->setCellValue("A$row", $row); + } + + $writer = new Html($spreadsheet); + $html = $writer->generateHTMLall(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('div'); + $tbl = $divs->item(0)->getElementsByTagName('table'); + $thd = $tbl->item(0)->getElementsByTagName('thead'); + self::assertCount(0, $thd); + //$trw = $thd->item(0)->getElementsByTagName('tr'); + //self::assertCount(2, $trw); + $tbd = $divs->item(0)->getElementsByTagName('tbody'); + self::assertCount(1, $tbd); + $trw = $tbd->item(0)->getElementsByTagName('tr'); + self::assertCount(100, $trw); + + $this->writeAndReload($spreadsheet, 'Html'); + } + + public function testWriteRepeatsInline() + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->getPageSetup()->setRowsToRepeatAtTop([1, 2]); + $sheet->setCellValue('A1', 'Repeat1'); + $sheet->setCellValue('A2', 'Repeat2'); + for ($row = 3; $row <= 100; ++$row) { + $sheet->setCellValue("A$row", $row); + } + + $writer = new Html($spreadsheet); + self::assertFalse($writer->getUseInlineCss()); + $writer->setUseInlineCss(true); + $html = $writer->generateHTMLall(); + $dom = new \DOMDocument(); + $dom->loadHTML($html); + $body = $dom->getElementsByTagName('body')[0]; + $divs = $body->getElementsByTagName('div'); + $tbl = $divs->item(0)->getElementsByTagName('table'); + self::assertEquals('border-collapse:collapse', $tbl->item(0)->getAttribute('style')); + $thd = $divs->item(0)->getElementsByTagName('thead'); + self::assertCount(1, $thd); + $trw = $thd->item(0)->getElementsByTagName('tr'); + self::assertCount(2, $trw); + $tbd = $divs->item(0)->getElementsByTagName('tbody'); + self::assertCount(1, $tbd); + $trw = $tbd->item(0)->getElementsByTagName('tr'); + self::assertCount(98, $trw); + + $this->writeAndReload($spreadsheet, 'Html'); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Html/VisibilityTest.php b/tests/PhpSpreadsheetTests/Writer/Html/VisibilityTest.php new file mode 100644 index 0000000000..63cdefb7d9 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/VisibilityTest.php @@ -0,0 +1,111 @@ +getActiveSheet(); + $sheet->setCellValue('A1', 1); + $sheet->setCellValue('A2', 2); + $sheet->setCellValue('A3', 3); + $sheet->setCellValue('B1', 4); + $sheet->setCellValue('B2', 5); + $sheet->setCellValue('B3', 6); + $sheet->setCellValue('C1', 7); + $sheet->setCellValue('C2', 8); + $sheet->setCellValue('C3', 9); + $sheet->getColumnDimension('B')->setVisible(false); + $sheet->getRowDimension(2)->setVisible(false); + $writer = new Html($spreadsheet); + $html = $writer->generateHTMLAll(); + $reg = '/^\\s*table[.]sheet0 tr { display:none; visibility:hidden }\\s*$/m'; + $rowsrch = preg_match($reg, $html); + self::assertEquals($rowsrch, 0); + $reg = '/^\\s*table[.]sheet0 tr[.]row1 { display:none; visibility:hidden }\\s*$/m'; + $rowsrch = preg_match($reg, $html); + self::assertEquals($rowsrch, 1); + $reg = '/^\\s*table[.]sheet0 [.]column1 [{] display:none [}]\\s*$/m'; + $colsrch = preg_match($reg, $html); + self::assertEquals($colsrch, 1); + + $this->writeAndReload($spreadsheet, 'Html'); + } + + public function testVisibility2() + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setCellValue('A1', 1); + $sheet->setCellValue('A2', 2); + $sheet->setCellValue('A3', 3); + $sheet->setCellValue('B1', 4); + $sheet->setCellValue('B2', 5); + $sheet->setCellValue('B3', 6); + $sheet->setCellValue('C1', 7); + $sheet->setCellValue('C2', 8); + $sheet->setCellValue('C3', 9); + $sheet->getDefaultRowDimension()->setVisible(false); + $sheet->getColumnDimension('B')->setVisible(false); + $sheet->getRowDimension(1)->setVisible(true); + $sheet->getRowDimension(3)->setVisible(true); + + $writer = new Html($spreadsheet); + $html = $writer->generateHTMLAll(); + $reg = '/^\\s*table[.]sheet0 tr { height:15pt; display:none; visibility:hidden }\\s*$/m'; + $rowsrch = preg_match($reg, $html); + self::assertEquals($rowsrch, 1); + $reg = '/^\\s*table[.]sheet0 tr[.]row1 { display:none; visibility:hidden }\\s*$/m'; + $rowsrch = preg_match($reg, $html); + self::assertEquals($rowsrch, 0); + $reg = '/^\\s*table[.]sheet0 [.]column1 [{] display:none [}]\\s*$/m'; + $colsrch = preg_match($reg, $html); + self::assertEquals($colsrch, 1); + + $this->writeAndReload($spreadsheet, 'Html'); + } + + public function testDefaultRowHeight() + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setCellValue('A1', 1); + $sheet->getStyle('A1')->getFont()->setStrikethrough(true); + $sheet->setCellValue('A2', 2); + $sheet->setCellValue('A3', 3); + $sheet->getStyle('A3')->getFont()->setStrikethrough(true)->setUnderline(Font::UNDERLINE_SINGLE); + $sheet->setCellValue('B1', 4); + $sheet->setCellValue('B2', 5); + $sheet->setCellValue('B3', 6); + $sheet->setCellValue('C1', 7); + $sheet->setCellValue('C2', 8); + $sheet->setCellValue('C3', 9); + $sheet->getStyle('C3')->getFont()->setUnderline(Font::UNDERLINE_SINGLE); + $sheet->getDefaultRowDimension()->setRowHeight(20); + $sheet->getRowDimension(2)->setRowHeight(25); + + $writer = new Html($spreadsheet); + $html = $writer->generateHTMLAll(); + self::assertEquals(1, substr_count($html, 'height:20pt')); + self::assertEquals(1, substr_count($html, 'height:25pt')); + $rowsrch = preg_match('/^\\s*table[.]sheet0 tr [{] height:20pt [}]\\s*$/m', $html); + self::assertEquals(1, $rowsrch); + $rowsrch = preg_match('/^\\s*table[.]sheet0 tr[.]row1 [{] height:25pt [}]\\s*$/m', $html); + self::assertEquals(1, $rowsrch); + $rowsrch = preg_match('/^\\s*td[.]style1 [{].*text-decoration:line-through;.*[}]\\s*$/m', $html); + self::assertEquals(1, $rowsrch); + $rowsrch = preg_match('/^\\s*td[.]style2 [{].*text-decoration:underline line-through;.*[}]\\s*$/m', $html); + self::assertEquals(1, $rowsrch); + $rowsrch = preg_match('/^\\s*td[.]style3 [{].*text-decoration:underline;.*[}]\\s*$/m', $html); + self::assertEquals(1, $rowsrch); + + $this->writeAndReload($spreadsheet, 'Html'); + } +} diff --git a/tests/data/Style/NumberFormat.php b/tests/data/Style/NumberFormat.php index d9ab094094..5038d6da47 100644 --- a/tests/data/Style/NumberFormat.php +++ b/tests/data/Style/NumberFormat.php @@ -103,6 +103,11 @@ 12345.678900000001, '#,##0.000\ [$€-1]', ], + [ + '12,345.679 $', + 12345.678900000001, + '#,##0.000\ [$]', + ], [ '5.68', 5.6788999999999996, @@ -177,6 +182,21 @@ 5.25, '???/???', ], + [ + '0 3/4', + 0.75, + '0??/???', + ], + [ + '3/4', + 0.75, + '#??/???', + ], + [ + ' 3/4', + 0.75, + '? ??/???', + ], // Complex formats [ '(001) 2-3456-789', @@ -302,4 +322,24 @@ -2, '[Green]"Positive";[Red]"Negative";[Blue]"Zero"', ], + [ + '<=3500 red', + 3500, + '[Green][=17]"=17 green";[Red][<=3500]"<=3500 red";[Blue]"Zero"', + ], + [ + '=17 green', + 17, + '[Green][=17]"=17 green";[Red][<=3500]"<=3500 red";[Blue]"Zero"', + ], + [ + '<>25 green', + 17, + '[Green][<>25]"<>25 green";[Red]"else red"', + ], + [ + 'else red', + 25, + '[Green][<>25]"<>25 green";[Red]"else red"', + ], ];