diff --git a/composer.lock b/composer.lock index 4ac9f631..3f82754d 100644 --- a/composer.lock +++ b/composer.lock @@ -763,12 +763,12 @@ "source": { "type": "git", "url": "https://github.com/PHPCSStandards/composer-installer.git", - "reference": "04f4e8f6716241cb9200774ff73cb99fbb81e09a" + "reference": "231b4e82eee01f16537c8ac3fce31c1f83320c80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/04f4e8f6716241cb9200774ff73cb99fbb81e09a", - "reference": "04f4e8f6716241cb9200774ff73cb99fbb81e09a", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/231b4e82eee01f16537c8ac3fce31c1f83320c80", + "reference": "231b4e82eee01f16537c8ac3fce31c1f83320c80", "shasum": "" }, "require": { @@ -829,7 +829,7 @@ "stylecheck", "tests" ], - "time": "2022-06-26T10:27:07+00:00" + "time": "2022-07-26T12:51:47+00:00" }, { "name": "doctrine/annotations", @@ -2076,16 +2076,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.8.0", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b7648d4ee9321665acaf112e49da9fd93df8fbd5" + "reference": "c53312ecc575caf07b0e90dee43883fdf90ca67c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b7648d4ee9321665acaf112e49da9fd93df8fbd5", - "reference": "b7648d4ee9321665acaf112e49da9fd93df8fbd5", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c53312ecc575caf07b0e90dee43883fdf90ca67c", + "reference": "c53312ecc575caf07b0e90dee43883fdf90ca67c", "shasum": "" }, "require": { @@ -2127,7 +2127,7 @@ "type": "tidelift" } ], - "time": "2022-06-29T08:53:31+00:00" + "time": "2022-07-20T09:57:31+00:00" }, { "name": "phpstan/phpstan-phpunit", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 024f1fbd..49ab167e 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2270,51 +2270,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php - - - message: "#^Cannot access offset 0 on array\\|false\\.$#" - count: 1 - path: src/PhpSpreadsheet/Shared/Font.php - - - - message: "#^Cannot access offset 2 on array\\|false\\.$#" - count: 1 - path: src/PhpSpreadsheet/Shared/Font.php - - - - message: "#^Cannot access offset 4 on array\\|false\\.$#" - count: 1 - path: src/PhpSpreadsheet/Shared/Font.php - - - - message: "#^Cannot access offset 6 on array\\|false\\.$#" - count: 1 - path: src/PhpSpreadsheet/Shared/Font.php - - - - message: "#^Parameter \\#1 \\$size of function imagettfbbox expects float, float\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Shared/Font.php - - - - message: "#^Parameter \\#2 \\$defaultFont of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Drawing\\:\\:pixelsToCellDimension\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font, PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Shared/Font.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Font\\:\\:\\$autoSizeMethods has no type specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Shared/Font.php - - - - message: "#^Unreachable statement \\- code above always terminates\\.$#" - count: 1 - path: src/PhpSpreadsheet/Shared/Font.php - - - - message: "#^Variable \\$cellText on left side of \\?\\? always exists and is not nullable\\.$#" - count: 1 - path: src/PhpSpreadsheet/Shared/Font.php - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\EigenvalueDecomposition\\:\\:\\$cdivi has no type specified\\.$#" count: 1 diff --git a/samples/Basic/26_Utf8.php b/samples/Basic/26_Utf8.php index 52953251..52a64509 100644 --- a/samples/Basic/26_Utf8.php +++ b/samples/Basic/26_Utf8.php @@ -12,12 +12,10 @@ $spreadsheet = $reader->load(__DIR__ . '/../templates/26template.xlsx'); // at this point, we could do some manipulations with the template, but we skip this step $helper->write($spreadsheet, __FILE__, ['Xlsx', 'Xls', 'Html']); -if (\PHP_VERSION_ID < 80000) { - // Export to PDF (.pdf) - $helper->log('Write to PDF format'); - IOFactory::registerWriter('Pdf', \PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf::class); - $helper->write($spreadsheet, __FILE__, ['Pdf']); -} +// Export to PDF (.pdf) +$helper->log('Write to PDF format'); +IOFactory::registerWriter('Pdf', \PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf::class); +$helper->write($spreadsheet, __FILE__, ['Pdf']); // Remove first two rows with field headers before exporting to CSV $helper->log('Removing first two heading rows for CSV export'); diff --git a/samples/Pdf/21b_Pdf.php b/samples/Pdf/21b_Pdf.php index ad2f609b..c67ff3d2 100644 --- a/samples/Pdf/21b_Pdf.php +++ b/samples/Pdf/21b_Pdf.php @@ -32,13 +32,11 @@ $spreadsheet->getActiveSheet()->setShowGridLines(false); $helper->log('Set orientation to landscape'); $spreadsheet->getActiveSheet()->getPageSetup()->setOrientation(PageSetup::ORIENTATION_LANDSCAPE); -if (\PHP_VERSION_ID < 80000) { - $helper->log('Write to Dompdf'); - $writer = new Dompdf($spreadsheet); - $filename = $helper->getFileName('21b_Pdf_dompdf.xlsx', 'pdf'); - $writer->setEditHtmlCallback('replaceBody'); - $writer->save($filename); -} +$helper->log('Write to Dompdf'); +$writer = new Dompdf($spreadsheet); +$filename = $helper->getFileName('21b_Pdf_dompdf.xlsx', 'pdf'); +$writer->setEditHtmlCallback('replaceBody'); +$writer->save($filename); $helper->log('Write to Mpdf'); $writer = new Mpdf($spreadsheet); @@ -46,10 +44,8 @@ $filename = $helper->getFileName('21b_Pdf_mpdf.xlsx', 'pdf'); $writer->setEditHtmlCallback('replaceBody'); $writer->save($filename); -if (\PHP_VERSION_ID < 80000) { - $helper->log('Write to Tcpdf'); - $writer = new Tcpdf($spreadsheet); - $filename = $helper->getFileName('21b_Pdf_tcpdf.xlsx', 'pdf'); - $writer->setEditHtmlCallback('replaceBody'); - $writer->save($filename); -} +$helper->log('Write to Tcpdf'); +$writer = new Tcpdf($spreadsheet); +$filename = $helper->getFileName('21b_Pdf_tcpdf.xlsx', 'pdf'); +$writer->setEditHtmlCallback('replaceBody'); +$writer->save($filename); diff --git a/src/PhpSpreadsheet/Calculation/TextData/Extract.php b/src/PhpSpreadsheet/Calculation/TextData/Extract.php index 1a9e84db..ee7e31b7 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Extract.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Extract.php @@ -104,7 +104,8 @@ class Extract * * @param mixed $text the text that you're searching * Or can be an array of values - * @param ?string $delimiter the text that marks the point before which you want to extract + * @param null|array|string $delimiter the text that marks the point before which you want to extract + * Multiple delimiters can be passed as an array of string values * @param mixed $instance The instance of the delimiter after which you want to extract the text. * By default, this is the first instance (1). * A negative value means start searching from the end of the text string. @@ -132,7 +133,6 @@ class Extract } $text = Helpers::extractString($text ?? ''); - $delimiter = Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')); $instance = (int) $instance; $matchMode = (int) $matchMode; $matchEnd = (int) $matchEnd; @@ -141,13 +141,14 @@ class Extract if (is_array($split) === false) { return $split; } - if ($delimiter === '') { + if (Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')) === '') { return ($instance > 0) ? '' : $text; } // Adjustment for a match as the first element of the split $flags = self::matchFlags($matchMode); - $adjust = preg_match('/^' . preg_quote($delimiter) . "\$/{$flags}", $split[0]); + $delimiter = self::buildDelimiter($delimiter); + $adjust = preg_match('/^' . $delimiter . "\$/{$flags}", $split[0]); $oddReverseAdjustment = count($split) % 2; $split = ($instance < 0) @@ -161,7 +162,8 @@ class Extract * TEXTAFTER. * * @param mixed $text the text that you're searching - * @param ?string $delimiter the text that marks the point before which you want to extract + * @param null|array|string $delimiter the text that marks the point before which you want to extract + * Multiple delimiters can be passed as an array of string values * @param mixed $instance The instance of the delimiter after which you want to extract the text. * By default, this is the first instance (1). * A negative value means start searching from the end of the text string. @@ -189,7 +191,6 @@ class Extract } $text = Helpers::extractString($text ?? ''); - $delimiter = Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')); $instance = (int) $instance; $matchMode = (int) $matchMode; $matchEnd = (int) $matchEnd; @@ -198,13 +199,14 @@ class Extract if (is_array($split) === false) { return $split; } - if ($delimiter === '') { + if (Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')) === '') { return ($instance < 0) ? '' : $text; } // Adjustment for a match as the first element of the split $flags = self::matchFlags($matchMode); - $adjust = preg_match('/^' . preg_quote($delimiter) . "\$/{$flags}", $split[0]); + $delimiter = self::buildDelimiter($delimiter); + $adjust = preg_match('/^' . $delimiter . "\$/{$flags}", $split[0]); $oddReverseAdjustment = count($split) % 2; $split = ($instance < 0) @@ -215,21 +217,23 @@ class Extract } /** + * @param null|array|string $delimiter * @param int $matchMode * @param int $matchEnd * @param mixed $ifNotFound * * @return string|string[] */ - private static function validateTextBeforeAfter(string $text, string $delimiter, int $instance, $matchMode, $matchEnd, $ifNotFound) + private static function validateTextBeforeAfter(string $text, $delimiter, int $instance, $matchMode, $matchEnd, $ifNotFound) { $flags = self::matchFlags($matchMode); + $delimiter = self::buildDelimiter($delimiter); - if (preg_match('/' . preg_quote($delimiter) . "/{$flags}", $text) === 0 && $matchEnd === 0) { + if (preg_match('/' . $delimiter . "/{$flags}", $text) === 0 && $matchEnd === 0) { return $ifNotFound; } - $split = preg_split('/(' . preg_quote($delimiter) . ")/{$flags}", $text, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); + $split = preg_split('/' . $delimiter . "/{$flags}", $text, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); if ($split === false) { return ExcelError::NA(); } @@ -247,6 +251,28 @@ class Extract return $split; } + /** + * @param null|array|string $delimiter the text that marks the point before which you want to extract + * Multiple delimiters can be passed as an array of string values + */ + private static function buildDelimiter($delimiter): string + { + if (is_array($delimiter)) { + $delimiter = Functions::flattenArray($delimiter); + $quotedDelimiters = array_map( + function ($delimiter) { + return preg_quote($delimiter ?? ''); + }, + $delimiter + ); + $delimiters = implode('|', $quotedDelimiters); + + return '(' . $delimiters . ')'; + } + + return '(' . preg_quote($delimiter ?? '') . ')'; + } + private static function matchFlags(int $matchMode): string { return ($matchMode === 0) ? 'mu' : 'miu'; diff --git a/src/PhpSpreadsheet/Shared/Font.php b/src/PhpSpreadsheet/Shared/Font.php index 1adf213e..33796b4c 100644 --- a/src/PhpSpreadsheet/Shared/Font.php +++ b/src/PhpSpreadsheet/Shared/Font.php @@ -13,7 +13,7 @@ class Font const AUTOSIZE_METHOD_APPROX = 'approx'; const AUTOSIZE_METHOD_EXACT = 'exact'; - private static $autoSizeMethods = [ + private const AUTOSIZE_METHODS = [ self::AUTOSIZE_METHOD_APPROX, self::AUTOSIZE_METHOD_EXACT, ]; @@ -101,6 +101,105 @@ class Font const VERDANA_ITALIC = 'verdanai.ttf'; const VERDANA_BOLD_ITALIC = 'verdanaz.ttf'; + const FONT_FILE_NAMES = [ + 'Arial' => [ + 'x' => self::ARIAL, + 'xb' => self::ARIAL_BOLD, + 'xi' => self::ARIAL_ITALIC, + 'xbi' => self::ARIAL_BOLD_ITALIC, + ], + 'Calibri' => [ + 'x' => self::CALIBRI, + 'xb' => self::CALIBRI_BOLD, + 'xi' => self::CALIBRI_ITALIC, + 'xbi' => self::CALIBRI_BOLD_ITALIC, + ], + 'Comic Sans MS' => [ + 'x' => self::COMIC_SANS_MS, + 'xb' => self::COMIC_SANS_MS_BOLD, + 'xi' => self::COMIC_SANS_MS, + 'xbi' => self::COMIC_SANS_MS_BOLD, + ], + 'Courier New' => [ + 'x' => self::COURIER_NEW, + 'xb' => self::COURIER_NEW_BOLD, + 'xi' => self::COURIER_NEW_ITALIC, + 'xbi' => self::COURIER_NEW_BOLD_ITALIC, + ], + 'Georgia' => [ + 'x' => self::GEORGIA, + 'xb' => self::GEORGIA_BOLD, + 'xi' => self::GEORGIA_ITALIC, + 'xbi' => self::GEORGIA_BOLD_ITALIC, + ], + 'Impact' => [ + 'x' => self::IMPACT, + 'xb' => self::IMPACT, + 'xi' => self::IMPACT, + 'xbi' => self::IMPACT, + ], + 'Liberation Sans' => [ + 'x' => self::LIBERATION_SANS, + 'xb' => self::LIBERATION_SANS_BOLD, + 'xi' => self::LIBERATION_SANS_ITALIC, + 'xbi' => self::LIBERATION_SANS_BOLD_ITALIC, + ], + 'Lucida Console' => [ + 'x' => self::LUCIDA_CONSOLE, + 'xb' => self::LUCIDA_CONSOLE, + 'xi' => self::LUCIDA_CONSOLE, + 'xbi' => self::LUCIDA_CONSOLE, + ], + 'Lucida Sans Unicode' => [ + 'x' => self::LUCIDA_SANS_UNICODE, + 'xb' => self::LUCIDA_SANS_UNICODE, + 'xi' => self::LUCIDA_SANS_UNICODE, + 'xbi' => self::LUCIDA_SANS_UNICODE, + ], + 'Microsoft Sans Serif' => [ + 'x' => self::MICROSOFT_SANS_SERIF, + 'xb' => self::MICROSOFT_SANS_SERIF, + 'xi' => self::MICROSOFT_SANS_SERIF, + 'xbi' => self::MICROSOFT_SANS_SERIF, + ], + 'Palatino Linotype' => [ + 'x' => self::PALATINO_LINOTYPE, + 'xb' => self::PALATINO_LINOTYPE_BOLD, + 'xi' => self::PALATINO_LINOTYPE_ITALIC, + 'xbi' => self::PALATINO_LINOTYPE_BOLD_ITALIC, + ], + 'Symbol' => [ + 'x' => self::SYMBOL, + 'xb' => self::SYMBOL, + 'xi' => self::SYMBOL, + 'xbi' => self::SYMBOL, + ], + 'Tahoma' => [ + 'x' => self::TAHOMA, + 'xb' => self::TAHOMA_BOLD, + 'xi' => self::TAHOMA, + 'xbi' => self::TAHOMA_BOLD, + ], + 'Times New Roman' => [ + 'x' => self::TIMES_NEW_ROMAN, + 'xb' => self::TIMES_NEW_ROMAN_BOLD, + 'xi' => self::TIMES_NEW_ROMAN_ITALIC, + 'xbi' => self::TIMES_NEW_ROMAN_BOLD_ITALIC, + ], + 'Trebuchet MS' => [ + 'x' => self::TREBUCHET_MS, + 'xb' => self::TREBUCHET_MS_BOLD, + 'xi' => self::TREBUCHET_MS_ITALIC, + 'xbi' => self::TREBUCHET_MS_BOLD_ITALIC, + ], + 'Verdana' => [ + 'x' => self::VERDANA, + 'xb' => self::VERDANA_BOLD, + 'xi' => self::VERDANA_ITALIC, + 'xbi' => self::VERDANA_BOLD_ITALIC, + ], + ]; + /** * AutoSize method. * @@ -113,54 +212,65 @@ class Font * * @var string */ - private static $trueTypeFontPath; + private static $trueTypeFontPath = ''; /** * How wide is a default column for a given default font and size? * Empirical data found by inspecting real Excel files and reading off the pixel width * in Microsoft Office Excel 2007. + * Added height in points. + */ + public const DEFAULT_COLUMN_WIDTHS = [ + 'Arial' => [ + 1 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25], + 2 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25], + 3 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.0], + + 4 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.75], + 5 => ['px' => 40, 'width' => 10.00000000, 'height' => 8.25], + 6 => ['px' => 48, 'width' => 9.59765625, 'height' => 8.25], + 7 => ['px' => 48, 'width' => 9.59765625, 'height' => 9.0], + 8 => ['px' => 56, 'width' => 9.33203125, 'height' => 11.25], + 9 => ['px' => 64, 'width' => 9.14062500, 'height' => 12.0], + 10 => ['px' => 64, 'width' => 9.14062500, 'height' => 12.75], + ], + 'Calibri' => [ + 1 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25], + 2 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25], + 3 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.00], + 4 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.75], + 5 => ['px' => 40, 'width' => 10.00000000, 'height' => 8.25], + 6 => ['px' => 48, 'width' => 9.59765625, 'height' => 8.25], + 7 => ['px' => 48, 'width' => 9.59765625, 'height' => 9.0], + 8 => ['px' => 56, 'width' => 9.33203125, 'height' => 11.25], + 9 => ['px' => 56, 'width' => 9.33203125, 'height' => 12.0], + 10 => ['px' => 64, 'width' => 9.14062500, 'height' => 12.75], + 11 => ['px' => 64, 'width' => 9.14062500, 'height' => 15.0], + ], + 'Verdana' => [ + 1 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25], + 2 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25], + 3 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.0], + 4 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.75], + 5 => ['px' => 40, 'width' => 10.00000000, 'height' => 8.25], + 6 => ['px' => 48, 'width' => 9.59765625, 'height' => 8.25], + 7 => ['px' => 48, 'width' => 9.59765625, 'height' => 9.0], + 8 => ['px' => 64, 'width' => 9.14062500, 'height' => 10.5], + 9 => ['px' => 72, 'width' => 9.00000000, 'height' => 11.25], + 10 => ['px' => 72, 'width' => 9.00000000, 'height' => 12.75], + ], + ]; + + /** + * List of column widths. Replaced by constant; + * previously it was public and updateable, allowing + * user to make inappropriate alterations. + * + * @deprecated 1.25.0 Use DEFAULT_COLUMN_WIDTHS constant instead. * * @var array */ - public static $defaultColumnWidths = [ - 'Arial' => [ - 1 => ['px' => 24, 'width' => 12.00000000], - 2 => ['px' => 24, 'width' => 12.00000000], - 3 => ['px' => 32, 'width' => 10.66406250], - 4 => ['px' => 32, 'width' => 10.66406250], - 5 => ['px' => 40, 'width' => 10.00000000], - 6 => ['px' => 48, 'width' => 9.59765625], - 7 => ['px' => 48, 'width' => 9.59765625], - 8 => ['px' => 56, 'width' => 9.33203125], - 9 => ['px' => 64, 'width' => 9.14062500], - 10 => ['px' => 64, 'width' => 9.14062500], - ], - 'Calibri' => [ - 1 => ['px' => 24, 'width' => 12.00000000], - 2 => ['px' => 24, 'width' => 12.00000000], - 3 => ['px' => 32, 'width' => 10.66406250], - 4 => ['px' => 32, 'width' => 10.66406250], - 5 => ['px' => 40, 'width' => 10.00000000], - 6 => ['px' => 48, 'width' => 9.59765625], - 7 => ['px' => 48, 'width' => 9.59765625], - 8 => ['px' => 56, 'width' => 9.33203125], - 9 => ['px' => 56, 'width' => 9.33203125], - 10 => ['px' => 64, 'width' => 9.14062500], - 11 => ['px' => 64, 'width' => 9.14062500], - ], - 'Verdana' => [ - 1 => ['px' => 24, 'width' => 12.00000000], - 2 => ['px' => 24, 'width' => 12.00000000], - 3 => ['px' => 32, 'width' => 10.66406250], - 4 => ['px' => 32, 'width' => 10.66406250], - 5 => ['px' => 40, 'width' => 10.00000000], - 6 => ['px' => 48, 'width' => 9.59765625], - 7 => ['px' => 48, 'width' => 9.59765625], - 8 => ['px' => 64, 'width' => 9.14062500], - 9 => ['px' => 72, 'width' => 9.00000000], - 10 => ['px' => 72, 'width' => 9.00000000], - ], - ]; + public static $defaultColumnWidths = self::DEFAULT_COLUMN_WIDTHS; /** * Set autoSize method. @@ -171,7 +281,7 @@ class Font */ public static function setAutoSizeMethod($method) { - if (!in_array($method, self::$autoSizeMethods)) { + if (!in_array($method, self::AUTOSIZE_METHODS)) { return false; } self::$autoSizeMethod = $method; @@ -219,7 +329,7 @@ class Font * Calculate an (approximate) OpenXML column width, based on font size and text contained. * * @param FontStyle $font Font object - * @param RichText|string $cellText Text to calculate width + * @param null|RichText|string $cellText Text to calculate width * @param int $rotation Rotation angle * @param null|FontStyle $defaultFont Font object * @param bool $filterAdjustment Add space for Autofilter or Table dropdown @@ -238,7 +348,8 @@ class Font } // Special case if there are one or more newline characters ("\n") - if (strpos($cellText ?? '', "\n") !== false) { + $cellText = $cellText ?? ''; + if (strpos($cellText, "\n") !== false) { $lineTexts = explode("\n", $cellText); $lineWidths = []; foreach ($lineTexts as $lineText) { @@ -281,7 +392,7 @@ class Font } // Convert from pixel width to column width - $columnWidth = Drawing::pixelsToCellDimension((int) $columnWidth, $defaultFont); + $columnWidth = Drawing::pixelsToCellDimension((int) $columnWidth, $defaultFont ?? new FontStyle()); // Return return (int) round($columnWidth, 6); @@ -299,7 +410,12 @@ class Font // font size should really be supplied in pixels in GD2, // but since GD2 seems to assume 72dpi, pixels and points are the same $fontFile = self::getTrueTypeFontFileFromFont($font); - $textBox = imagettfbbox($font->getSize(), $rotation, $fontFile, $text); + $textBox = imagettfbbox($font->getSize() ?? 10.0, $rotation, $fontFile, $text); + if ($textBox === false) { + // @codeCoverageIgnoreStart + throw new PhpSpreadsheetException('imagettfbbox failed'); + // @codeCoverageIgnoreEnd + } // Get corners positions $lowerLeftCornerX = $textBox[0]; @@ -409,129 +525,48 @@ class Font * * @return string Path to TrueType font file */ - public static function getTrueTypeFontFileFromFont(FontStyle $font) + public static function getTrueTypeFontFileFromFont(FontStyle $font, bool $checkPath = true) { - if (!file_exists(self::$trueTypeFontPath) || !is_dir(self::$trueTypeFontPath)) { + if ($checkPath && (!file_exists(self::$trueTypeFontPath) || !is_dir(self::$trueTypeFontPath))) { throw new PhpSpreadsheetException('Valid directory to TrueType Font files not specified'); } $name = $font->getName(); + if (!isset(self::FONT_FILE_NAMES[$name])) { + throw new PhpSpreadsheetException('Unknown font name "' . $name . '". Cannot map to TrueType font file'); + } $bold = $font->getBold(); $italic = $font->getItalic(); - - // Check if we can map font to true type font file - switch ($name) { - case 'Arial': - $fontFile = ( - $bold ? ($italic ? self::ARIAL_BOLD_ITALIC : self::ARIAL_BOLD) - : ($italic ? self::ARIAL_ITALIC : self::ARIAL) - ); - - break; - case 'Calibri': - $fontFile = ( - $bold ? ($italic ? self::CALIBRI_BOLD_ITALIC : self::CALIBRI_BOLD) - : ($italic ? self::CALIBRI_ITALIC : self::CALIBRI) - ); - - break; - case 'Courier New': - $fontFile = ( - $bold ? ($italic ? self::COURIER_NEW_BOLD_ITALIC : self::COURIER_NEW_BOLD) - : ($italic ? self::COURIER_NEW_ITALIC : self::COURIER_NEW) - ); - - break; - case 'Comic Sans MS': - $fontFile = ( - $bold ? self::COMIC_SANS_MS_BOLD : self::COMIC_SANS_MS - ); - - break; - case 'Georgia': - $fontFile = ( - $bold ? ($italic ? self::GEORGIA_BOLD_ITALIC : self::GEORGIA_BOLD) - : ($italic ? self::GEORGIA_ITALIC : self::GEORGIA) - ); - - break; - case 'Impact': - $fontFile = self::IMPACT; - - break; - case 'Liberation Sans': - $fontFile = ( - $bold ? ($italic ? self::LIBERATION_SANS_BOLD_ITALIC : self::LIBERATION_SANS_BOLD) - : ($italic ? self::LIBERATION_SANS_ITALIC : self::LIBERATION_SANS) - ); - - break; - case 'Lucida Console': - $fontFile = self::LUCIDA_CONSOLE; - - break; - case 'Lucida Sans Unicode': - $fontFile = self::LUCIDA_SANS_UNICODE; - - break; - case 'Microsoft Sans Serif': - $fontFile = self::MICROSOFT_SANS_SERIF; - - break; - case 'Palatino Linotype': - $fontFile = ( - $bold ? ($italic ? self::PALATINO_LINOTYPE_BOLD_ITALIC : self::PALATINO_LINOTYPE_BOLD) - : ($italic ? self::PALATINO_LINOTYPE_ITALIC : self::PALATINO_LINOTYPE) - ); - - break; - case 'Symbol': - $fontFile = self::SYMBOL; - - break; - case 'Tahoma': - $fontFile = ( - $bold ? self::TAHOMA_BOLD : self::TAHOMA - ); - - break; - case 'Times New Roman': - $fontFile = ( - $bold ? ($italic ? self::TIMES_NEW_ROMAN_BOLD_ITALIC : self::TIMES_NEW_ROMAN_BOLD) - : ($italic ? self::TIMES_NEW_ROMAN_ITALIC : self::TIMES_NEW_ROMAN) - ); - - break; - case 'Trebuchet MS': - $fontFile = ( - $bold ? ($italic ? self::TREBUCHET_MS_BOLD_ITALIC : self::TREBUCHET_MS_BOLD) - : ($italic ? self::TREBUCHET_MS_ITALIC : self::TREBUCHET_MS) - ); - - break; - case 'Verdana': - $fontFile = ( - $bold ? ($italic ? self::VERDANA_BOLD_ITALIC : self::VERDANA_BOLD) - : ($italic ? self::VERDANA_ITALIC : self::VERDANA) - ); - - break; - default: - throw new PhpSpreadsheetException('Unknown font name "' . $name . '". Cannot map to TrueType font file'); - - break; + $index = 'x'; + if ($bold) { + $index .= 'b'; } + if ($italic) { + $index .= 'i'; + } + $fontFile = self::FONT_FILE_NAMES[$name][$index]; - $fontFile = self::$trueTypeFontPath . $fontFile; + $separator = ''; + if (mb_strlen(self::$trueTypeFontPath) > 1 && mb_substr(self::$trueTypeFontPath, -1) !== '/' && mb_substr(self::$trueTypeFontPath, -1) !== '\\') { + $separator = DIRECTORY_SEPARATOR; + } + $fontFile = self::$trueTypeFontPath . $separator . $fontFile; // Check if file actually exists - if (!file_exists($fontFile)) { + if ($checkPath && !file_exists($fontFile)) { throw new PhpSpreadsheetException('TrueType Font file not found'); } return $fontFile; } + public const CHARSET_FROM_FONT_NAME = [ + 'EucrosiaUPC' => self::CHARSET_ANSI_THAI, + 'Wingdings' => self::CHARSET_SYMBOL, + 'Wingdings 2' => self::CHARSET_SYMBOL, + 'Wingdings 3' => self::CHARSET_SYMBOL, + ]; + /** * Returns the associated charset for the font name. * @@ -541,19 +576,7 @@ class Font */ public static function getCharsetFromFontName($fontName) { - switch ($fontName) { - // Add more cases. Check FONT records in real Excel files. - case 'EucrosiaUPC': - return self::CHARSET_ANSI_THAI; - case 'Wingdings': - return self::CHARSET_SYMBOL; - case 'Wingdings 2': - return self::CHARSET_SYMBOL; - case 'Wingdings 3': - return self::CHARSET_SYMBOL; - default: - return self::CHARSET_ANSI_LATIN; - } + return self::CHARSET_FROM_FONT_NAME[$fontName] ?? self::CHARSET_ANSI_LATIN; } /** @@ -567,17 +590,17 @@ class Font */ public static function getDefaultColumnWidthByFont(FontStyle $font, $returnAsPixels = false) { - if (isset(self::$defaultColumnWidths[$font->getName()][$font->getSize()])) { + if (isset(self::DEFAULT_COLUMN_WIDTHS[$font->getName()][$font->getSize()])) { // Exact width can be determined $columnWidth = $returnAsPixels ? - self::$defaultColumnWidths[$font->getName()][$font->getSize()]['px'] - : self::$defaultColumnWidths[$font->getName()][$font->getSize()]['width']; + self::DEFAULT_COLUMN_WIDTHS[$font->getName()][$font->getSize()]['px'] + : self::DEFAULT_COLUMN_WIDTHS[$font->getName()][$font->getSize()]['width']; } else { // We don't have data for this particular font and size, use approximation by // extrapolating from Calibri 11 $columnWidth = $returnAsPixels ? - self::$defaultColumnWidths['Calibri'][11]['px'] - : self::$defaultColumnWidths['Calibri'][11]['width']; + self::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['px'] + : self::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['width']; $columnWidth = $columnWidth * $font->getSize() / 11; // Round pixels to closest integer @@ -599,173 +622,14 @@ class Font */ public static function getDefaultRowHeightByFont(FontStyle $font) { - switch ($font->getName()) { - case 'Arial': - switch ($font->getSize()) { - case 10: - // inspection of Arial 10 workbook says 12.75pt ~17px - $rowHeight = 12.75; - - break; - case 9: - // inspection of Arial 9 workbook says 12.00pt ~16px - $rowHeight = 12; - - break; - case 8: - // inspection of Arial 8 workbook says 11.25pt ~15px - $rowHeight = 11.25; - - break; - case 7: - // inspection of Arial 7 workbook says 9.00pt ~12px - $rowHeight = 9; - - break; - case 6: - case 5: - // inspection of Arial 5,6 workbook says 8.25pt ~11px - $rowHeight = 8.25; - - break; - case 4: - // inspection of Arial 4 workbook says 6.75pt ~9px - $rowHeight = 6.75; - - break; - case 3: - // inspection of Arial 3 workbook says 6.00pt ~8px - $rowHeight = 6; - - break; - case 2: - case 1: - // inspection of Arial 1,2 workbook says 5.25pt ~7px - $rowHeight = 5.25; - - break; - default: - // use Arial 10 workbook as an approximation, extrapolation - $rowHeight = 12.75 * $font->getSize() / 10; - - break; - } - - break; - case 'Calibri': - switch ($font->getSize()) { - case 11: - // inspection of Calibri 11 workbook says 15.00pt ~20px - $rowHeight = 15; - - break; - case 10: - // inspection of Calibri 10 workbook says 12.75pt ~17px - $rowHeight = 12.75; - - break; - case 9: - // inspection of Calibri 9 workbook says 12.00pt ~16px - $rowHeight = 12; - - break; - case 8: - // inspection of Calibri 8 workbook says 11.25pt ~15px - $rowHeight = 11.25; - - break; - case 7: - // inspection of Calibri 7 workbook says 9.00pt ~12px - $rowHeight = 9; - - break; - case 6: - case 5: - // inspection of Calibri 5,6 workbook says 8.25pt ~11px - $rowHeight = 8.25; - - break; - case 4: - // inspection of Calibri 4 workbook says 6.75pt ~9px - $rowHeight = 6.75; - - break; - case 3: - // inspection of Calibri 3 workbook says 6.00pt ~8px - $rowHeight = 6.00; - - break; - case 2: - case 1: - // inspection of Calibri 1,2 workbook says 5.25pt ~7px - $rowHeight = 5.25; - - break; - default: - // use Calibri 11 workbook as an approximation, extrapolation - $rowHeight = 15 * $font->getSize() / 11; - - break; - } - - break; - case 'Verdana': - switch ($font->getSize()) { - case 10: - // inspection of Verdana 10 workbook says 12.75pt ~17px - $rowHeight = 12.75; - - break; - case 9: - // inspection of Verdana 9 workbook says 11.25pt ~15px - $rowHeight = 11.25; - - break; - case 8: - // inspection of Verdana 8 workbook says 10.50pt ~14px - $rowHeight = 10.50; - - break; - case 7: - // inspection of Verdana 7 workbook says 9.00pt ~12px - $rowHeight = 9.00; - - break; - case 6: - case 5: - // inspection of Verdana 5,6 workbook says 8.25pt ~11px - $rowHeight = 8.25; - - break; - case 4: - // inspection of Verdana 4 workbook says 6.75pt ~9px - $rowHeight = 6.75; - - break; - case 3: - // inspection of Verdana 3 workbook says 6.00pt ~8px - $rowHeight = 6; - - break; - case 2: - case 1: - // inspection of Verdana 1,2 workbook says 5.25pt ~7px - $rowHeight = 5.25; - - break; - default: - // use Verdana 10 workbook as an approximation, extrapolation - $rowHeight = 12.75 * $font->getSize() / 10; - - break; - } - - break; - default: - // just use Calibri as an approximation - $rowHeight = 15 * $font->getSize() / 11; - - break; + $name = $font->getName(); + $size = $font->getSize(); + if (isset(self::DEFAULT_COLUMN_WIDTHS[$name][$size])) { + $rowHeight = self::DEFAULT_COLUMN_WIDTHS[$name][$size]['height']; + } elseif ($name === 'Arial' || $name === 'Verdana') { + $rowHeight = self::DEFAULT_COLUMN_WIDTHS[$name][10]['height'] * $size / 10.0; + } else { + $rowHeight = self::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['height'] * $size / 11.0; } return $rowHeight; diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index da32025a..6e51b7a8 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -126,6 +126,13 @@ class Html extends BaseWriter */ protected $isPdf = false; + /** + * Is the current writer creating mPDF? + * + * @var bool + */ + protected $isMPdf = false; + /** * Generate the Navigation block. * @@ -1003,6 +1010,14 @@ class Html extends BaseWriter $css['padding-' . $textAlign] = (string) ((int) $alignment->getIndent() * 9) . 'px'; } } + $rotation = $alignment->getTextRotation(); + if ($rotation !== 0 && $rotation !== Alignment::TEXTROTATION_STACK_PHPSPREADSHEET) { + if ($this->isMPdf) { + $css['text-rotate'] = "$rotation"; + } else { + $css['transform'] = "rotate({$rotation}deg)"; + } + } return $css; } diff --git a/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php b/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php index fc96f904..bf9e28cb 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php +++ b/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php @@ -35,6 +35,9 @@ class Dompdf extends Pdf $orientation = ($orientation === PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P'; $printPaperSize = $this->getPaperSize() ?? $setup->getPaperSize(); $paperSize = self::$paperSizes[$printPaperSize] ?? PageSetup::getPaperSizeDefault(); + if (is_array($paperSize) && count($paperSize) === 2) { + $paperSize = [0.0, 0.0, $paperSize[0], $paperSize[1]]; + } $orientation = ($orientation == 'L') ? 'landscape' : 'portrait'; diff --git a/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php b/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php index 281e1a4f..d0ce9ed4 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php +++ b/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php @@ -8,6 +8,9 @@ use PhpOffice\PhpSpreadsheet\Writer\Pdf; class Mpdf extends Pdf { + /** @var bool */ + protected $isMPdf = true; + /** * Gets the implementation of external PDF library that should be used. * diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextAfterTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextAfterTest.php index b3b01d24..00483260 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextAfterTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextAfterTest.php @@ -2,8 +2,6 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\Calculation; - class TextAfterTest extends AllSetupTeardown { /** @@ -14,14 +12,17 @@ class TextAfterTest extends AllSetupTeardown $text = $arguments[0]; $delimiter = $arguments[1]; - $args = 'A1, A2'; + $args = (is_array($delimiter)) ? 'A1, {A2,A3}' : 'A1, A2'; $args .= (isset($arguments[2])) ? ", {$arguments[2]}" : ','; $args .= (isset($arguments[3])) ? ", {$arguments[3]}" : ','; $args .= (isset($arguments[4])) ? ", {$arguments[4]}" : ','; $worksheet = $this->getSheet(); $worksheet->getCell('A1')->setValue($text); - $worksheet->getCell('A2')->setValue($delimiter); + $worksheet->getCell('A2')->setValue((is_array($delimiter)) ? $delimiter[0] : $delimiter); + if (is_array($delimiter)) { + $worksheet->getCell('A3')->setValue($delimiter[1]); + } $worksheet->getCell('B1')->setValue("=TEXTAFTER({$args})"); $result = $worksheet->getCell('B1')->getCalculatedValue(); @@ -32,18 +33,4 @@ class TextAfterTest extends AllSetupTeardown { return require 'tests/data/Calculation/TextData/TEXTAFTER.php'; } - - public function testTextAfterWithArray(): void - { - $calculation = Calculation::getInstance(); - - $text = "Red Riding Hood's red riding hood"; - $delimiter = 'red'; - - $args = "\"{$text}\", \"{$delimiter}\", 1, {0;1}"; - - $formula = "=TEXTAFTER({$args})"; - $result = $calculation->_calculateFormulaValue($formula); - self::assertEquals([[' riding hood'], [" Riding Hood's red riding hood"]], $result); - } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextBeforeTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextBeforeTest.php index 17938b5e..37e46636 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextBeforeTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextBeforeTest.php @@ -12,14 +12,17 @@ class TextBeforeTest extends AllSetupTeardown $text = $arguments[0]; $delimiter = $arguments[1]; - $args = 'A1, A2'; + $args = (is_array($delimiter)) ? 'A1, {A2,A3}' : 'A1, A2'; $args .= (isset($arguments[2])) ? ", {$arguments[2]}" : ','; $args .= (isset($arguments[3])) ? ", {$arguments[3]}" : ','; $args .= (isset($arguments[4])) ? ", {$arguments[4]}" : ','; $worksheet = $this->getSheet(); $worksheet->getCell('A1')->setValue($text); - $worksheet->getCell('A2')->setValue($delimiter); + $worksheet->getCell('A2')->setValue((is_array($delimiter)) ? $delimiter[0] : $delimiter); + if (is_array($delimiter)) { + $worksheet->getCell('A3')->setValue($delimiter[1]); + } $worksheet->getCell('B1')->setValue("=TEXTBEFORE({$args})"); $result = $worksheet->getCell('B1')->getCalculatedValue(); diff --git a/tests/PhpSpreadsheetTests/Functional/StreamTest.php b/tests/PhpSpreadsheetTests/Functional/StreamTest.php index 3911aaa6..a84a2490 100644 --- a/tests/PhpSpreadsheetTests/Functional/StreamTest.php +++ b/tests/PhpSpreadsheetTests/Functional/StreamTest.php @@ -17,12 +17,13 @@ class StreamTest extends TestCase ['Csv'], ['Html'], ['Mpdf'], + ['Dompdf'], ]; if (\PHP_VERSION_ID < 80000) { $providerFormats = array_merge( $providerFormats, - [['Tcpdf'], ['Dompdf']] + [['Tcpdf']] ); } diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/Rc4Test.php b/tests/PhpSpreadsheetTests/Reader/Xls/Rc4Test.php new file mode 100644 index 00000000..c3056c08 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xls/Rc4Test.php @@ -0,0 +1,21 @@ +RC4($string)); + $expectedResult = '2ac2fecdd8fbb84638e3a4820eb205cc8e29c28b9d5d6b2ef974f311964971c90e8b9ca16467ef2dc6fc3520'; + self::assertSame($expectedResult, $result); + } +} diff --git a/tests/PhpSpreadsheetTests/Shared/Font2Test.php b/tests/PhpSpreadsheetTests/Shared/Font2Test.php new file mode 100644 index 00000000..4bb247cf --- /dev/null +++ b/tests/PhpSpreadsheetTests/Shared/Font2Test.php @@ -0,0 +1,149 @@ +providerCharsetFromFontName(); + foreach ($tests as $test) { + $thisTest = $test[0]; + if (array_key_exists($thisTest, $covered)) { + $covered[$thisTest] = 1; + } else { + $defaultCovered = true; + } + } + foreach ($covered as $key => $val) { + self::assertEquals(1, $val, "FontName $key not tested"); + } + self::assertTrue($defaultCovered, 'Default key not tested'); + } + + public function providerCharsetFromFontName(): array + { + return [ + ['EucrosiaUPC', Font::CHARSET_ANSI_THAI], + ['Wingdings', Font::CHARSET_SYMBOL], + ['Wingdings 2', Font::CHARSET_SYMBOL], + ['Wingdings 3', Font::CHARSET_SYMBOL], + ['Default', Font::CHARSET_ANSI_LATIN], + ]; + } + + public function testColumnWidths(): void + { + $widths = Font::DEFAULT_COLUMN_WIDTHS; + $fontNames = ['Arial', 'Calibri', 'Verdana']; + $font = new StyleFont(); + foreach ($fontNames as $fontName) { + $font->setName($fontName); + $array = $widths[$fontName]; + foreach ($array as $points => $array2) { + $font->setSize($points); + $px = $array2['px']; + $width = $array2['width']; + self::assertEquals($px, Font::getDefaultColumnWidthByFont($font, true), "$fontName $points px"); + self::assertEquals($width, Font::getDefaultColumnWidthByFont($font, false), "$fontName $points ooxml-units"); + } + } + $pxCalibri11 = $widths['Calibri'][11]['px']; + $widthCalibri11 = $widths['Calibri'][11]['width']; + $fontName = 'unknown'; + $points = 11; + $font->setName($fontName); + $font->setSize($points); + self::assertEquals($pxCalibri11, Font::getDefaultColumnWidthByFont($font, true), "$fontName $points px"); + self::assertEquals($widthCalibri11, Font::getDefaultColumnWidthByFont($font, false), "$fontName $points ooxml-units"); + $points = 22; + $font->setSize($points); + self::assertEquals(2 * $pxCalibri11, Font::getDefaultColumnWidthByFont($font, true), "$fontName $points px"); + self::assertEquals(2 * $widthCalibri11, Font::getDefaultColumnWidthByFont($font, false), "$fontName $points ooxml-units"); + $fontName = 'Arial'; + $points = 33; + $font->setName($fontName); + $font->setSize($points); + self::assertEquals(3 * $pxCalibri11, Font::getDefaultColumnWidthByFont($font, true), "$fontName $points px"); + self::assertEquals(3 * $widthCalibri11, Font::getDefaultColumnWidthByFont($font, false), "$fontName $points ooxml-units"); + } + + public function testRowHeights(): void + { + $heights = Font::DEFAULT_COLUMN_WIDTHS; + $fontNames = ['Arial', 'Calibri', 'Verdana']; + $font = new StyleFont(); + foreach ($fontNames as $fontName) { + $font->setName($fontName); + $array = $heights[$fontName]; + foreach ($array as $points => $array2) { + $font->setSize($points); + $height = $array2['height']; + self::assertEquals($height, Font::getDefaultRowHeightByFont($font), "$fontName $points points"); + } + } + $heightArial10 = $heights['Arial'][10]['height']; + $fontName = 'Arial'; + $points = 20; + $font->setName($fontName); + $font->setSize($points); + self::assertEquals(2 * $heightArial10, Font::getDefaultRowHeightByFont($font), "$fontName $points points"); + $heightVerdana10 = $heights['Verdana'][10]['height']; + $fontName = 'Verdana'; + $points = 30; + $font->setName($fontName); + $font->setSize($points); + self::assertEquals(3 * $heightVerdana10, Font::getDefaultRowHeightByFont($font), "$fontName $points points"); + $heightCalibri11 = $heights['Calibri'][11]['height']; + $fontName = 'Calibri'; + $points = 22; + $font->setName($fontName); + $font->setSize($points); + self::assertEquals(2 * $heightCalibri11, Font::getDefaultRowHeightByFont($font), "$fontName $points points"); + $fontName = 'unknown'; + $points = 33; + $font->setName($fontName); + $font->setSize($points); + self::assertEquals(3 * $heightCalibri11, Font::getDefaultRowHeightByFont($font), "$fontName $points points"); + } + + public function testGetTrueTypeFontFileFromFont(): void + { + $fileNames = Font::FONT_FILE_NAMES; + $font = new StyleFont(); + foreach ($fileNames as $fontName => $fontNameArray) { + $font->setName($fontName); + $font->setBold(false); + $font->setItalic(false); + self::assertSame($fileNames[$fontName]['x'], Font::getTrueTypeFontFileFromFont($font, false), "$fontName not bold not italic"); + $font->setBold(true); + $font->setItalic(false); + self::assertSame($fileNames[$fontName]['xb'], Font::getTrueTypeFontFileFromFont($font, false), "$fontName bold not italic"); + $font->setBold(false); + $font->setItalic(true); + self::assertSame($fileNames[$fontName]['xi'], Font::getTrueTypeFontFileFromFont($font, false), "$fontName not bold italic"); + $font->setBold(true); + $font->setItalic(true); + self::assertSame($fileNames[$fontName]['xbi'], Font::getTrueTypeFontFileFromFont($font, false), "$fontName bold italic"); + } + } +} diff --git a/tests/PhpSpreadsheetTests/Shared/Font3Test.php b/tests/PhpSpreadsheetTests/Shared/Font3Test.php new file mode 100644 index 00000000..e91e7acf --- /dev/null +++ b/tests/PhpSpreadsheetTests/Shared/Font3Test.php @@ -0,0 +1,53 @@ +holdDirectory = Font::getTrueTypeFontPath(); + } + + protected function tearDown(): void + { + Font::setTrueTypeFontPath($this->holdDirectory); + } + + public function testGetTrueTypeException1(): void + { + $this->expectException(SSException::class); + $this->expectExceptionMessage('Valid directory to TrueType Font files not specified'); + $font = new StyleFont(); + $font->setName('unknown'); + Font::getTrueTypeFontFileFromFont($font); + } + + public function testGetTrueTypeException2(): void + { + Font::setTrueTypeFontPath(__DIR__); + $this->expectException(SSException::class); + $this->expectExceptionMessage('Unknown font name'); + $font = new StyleFont(); + $font->setName('unknown'); + Font::getTrueTypeFontFileFromFont($font); + } + + public function testGetTrueTypeException3(): void + { + Font::setTrueTypeFontPath(__DIR__); + $this->expectException(SSException::class); + $this->expectExceptionMessage('TrueType Font file not found'); + $font = new StyleFont(); + $font->setName('Calibri'); + Font::getTrueTypeFontFileFromFont($font); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Dompdf/PaperSizeArrayTest.php b/tests/PhpSpreadsheetTests/Writer/Dompdf/PaperSizeArrayTest.php new file mode 100644 index 00000000..815d7e3d --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Dompdf/PaperSizeArrayTest.php @@ -0,0 +1,65 @@ +outfile !== '') { + unlink($this->outfile); + $this->outfile = ''; + } + } + + public function testPaperSizeArray(): void + { + // Issue 1713 - array in PhpSpreadsheet is 2 elements, + // but in Dompdf it is 4 elements, first 2 are zero. + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + // TABLOID is a 2-element array in Writer/Pdf.php $paperSizes + $size = PageSetup::PAPERSIZE_TABLOID; + $sheet->getPageSetup()->setPaperSize($size); + $sheet->setPrintGridlines(true); + $sheet->getStyle('A7')->getAlignment()->setTextRotation(90); + $sheet->setCellValue('A7', 'Lorem Ipsum'); + $writer = new Dompdf($spreadsheet); + $this->outfile = File::temporaryFilename(); + $writer->save($this->outfile); + $spreadsheet->disconnectWorksheets(); + unset($spreadsheet); + $contents = file_get_contents($this->outfile); + self::assertNotFalse($contents); + self::assertStringContainsString('/MediaBox [0.000 0.000 792.000 1224.000]', $contents); + } + + public function testPaperSizeNotArray(): void + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + // LETTER is a string in Writer/Pdf.php $paperSizes + $size = PageSetup::PAPERSIZE_LETTER; + $sheet->getPageSetup()->setPaperSize($size); + $sheet->setPrintGridlines(true); + $sheet->getStyle('A7')->getAlignment()->setTextRotation(90); + $sheet->setCellValue('A7', 'Lorem Ipsum'); + $writer = new Dompdf($spreadsheet); + $this->outfile = File::temporaryFilename(); + $writer->save($this->outfile); + $spreadsheet->disconnectWorksheets(); + unset($spreadsheet); + $contents = file_get_contents($this->outfile); + self::assertNotFalse($contents); + self::assertStringContainsString('/MediaBox [0.000 0.000 612.000 792.000]', $contents); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Dompdf/TextRotationTest.php b/tests/PhpSpreadsheetTests/Writer/Dompdf/TextRotationTest.php new file mode 100644 index 00000000..2aa9d5ed --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Dompdf/TextRotationTest.php @@ -0,0 +1,24 @@ +getActiveSheet(); + $sheet->setPrintGridlines(true); + $sheet->getStyle('A7')->getAlignment()->setTextRotation(90); + $sheet->setCellValue('A7', 'Lorem Ipsum'); + $writer = new Dompdf($spreadsheet); + $html = $writer->generateHtmlAll(); + self::assertStringContainsString(' transform:rotate(90deg);', $html); + $spreadsheet->disconnectWorksheets(); + unset($spreadsheet); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Html/TextRotationTest.php b/tests/PhpSpreadsheetTests/Writer/Html/TextRotationTest.php new file mode 100644 index 00000000..da928457 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/TextRotationTest.php @@ -0,0 +1,24 @@ +getActiveSheet(); + $sheet->setPrintGridlines(true); + $sheet->getStyle('A7')->getAlignment()->setTextRotation(90); + $sheet->setCellValue('A7', 'Lorem Ipsum'); + $writer = new Html($spreadsheet); + $html = $writer->generateHtmlAll(); + self::assertStringContainsString(' transform:rotate(90deg);', $html); + $spreadsheet->disconnectWorksheets(); + unset($spreadsheet); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Mpdf/TextRotationTest.php b/tests/PhpSpreadsheetTests/Writer/Mpdf/TextRotationTest.php new file mode 100644 index 00000000..000a33b4 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Mpdf/TextRotationTest.php @@ -0,0 +1,24 @@ +getActiveSheet(); + $sheet->setPrintGridlines(true); + $sheet->getStyle('A7')->getAlignment()->setTextRotation(90); + $sheet->setCellValue('A7', 'Lorem Ipsum'); + $writer = new Mpdf($spreadsheet); + $html = $writer->generateHtmlAll(); + self::assertStringContainsString(' text-rotate:90;', $html); + $spreadsheet->disconnectWorksheets(); + unset($spreadsheet); + } +} diff --git a/tests/data/Calculation/TextData/TEXTAFTER.php b/tests/data/Calculation/TextData/TEXTAFTER.php index c594ecdc..ebcfecb9 100644 --- a/tests/data/Calculation/TextData/TEXTAFTER.php +++ b/tests/data/Calculation/TextData/TEXTAFTER.php @@ -212,4 +212,40 @@ return [ 1, ], ], + 'Multi-delimiter Case-Insensitive Offset 1' => [ + " riding hood's red riding hood", + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + 1, + 1, + ], + ], + 'Multi-delimiter Case-Insensitive Offset 2' => [ + "'s red riding hood", + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + 2, + 1, + ], + ], + 'Multi-delimiter Case-Insensitive Offset 3' => [ + ' riding hood', + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + 3, + 1, + ], + ], + 'Multi-delimiter Case-Insensitive Offset -2' => [ + ' riding hood', + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + -2, + 1, + ], + ], ]; diff --git a/tests/data/Calculation/TextData/TEXTBEFORE.php b/tests/data/Calculation/TextData/TEXTBEFORE.php index f94d5f28..1929354c 100644 --- a/tests/data/Calculation/TextData/TEXTBEFORE.php +++ b/tests/data/Calculation/TextData/TEXTBEFORE.php @@ -204,4 +204,40 @@ return [ 1, ], ], + 'Multi-delimiter Case-Insensitive Offset 1' => [ + 'Little ', + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + 1, + 1, + ], + ], + 'Multi-delimiter Case-Insensitive Offset 2' => [ + 'Little Red riding ', + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + 2, + 1, + ], + ], + 'Multi-delimiter Case-Insensitive Offset 3' => [ + "Little Red riding hood's ", + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + 3, + 1, + ], + ], + 'Multi-delimiter Case-Insensitive Offset -2' => [ + "Little Red riding hood's ", + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + -2, + 1, + ], + ], ];