updated-packages

This commit is contained in:
RafficMohammed
2023-01-08 00:13:22 +05:30
parent 3ff7df7487
commit da241bacb6
12659 changed files with 563377 additions and 510538 deletions

View File

@@ -0,0 +1,482 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Style;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
class Alignment extends Supervisor
{
// Horizontal alignment styles
const HORIZONTAL_GENERAL = 'general';
const HORIZONTAL_LEFT = 'left';
const HORIZONTAL_RIGHT = 'right';
const HORIZONTAL_CENTER = 'center';
const HORIZONTAL_CENTER_CONTINUOUS = 'centerContinuous';
const HORIZONTAL_JUSTIFY = 'justify';
const HORIZONTAL_FILL = 'fill';
const HORIZONTAL_DISTRIBUTED = 'distributed'; // Excel2007 only
// Vertical alignment styles
const VERTICAL_BOTTOM = 'bottom';
const VERTICAL_TOP = 'top';
const VERTICAL_CENTER = 'center';
const VERTICAL_JUSTIFY = 'justify';
const VERTICAL_DISTRIBUTED = 'distributed'; // Excel2007 only
// Read order
const READORDER_CONTEXT = 0;
const READORDER_LTR = 1;
const READORDER_RTL = 2;
// Special value for Text Rotation
const TEXTROTATION_STACK_EXCEL = 255;
const TEXTROTATION_STACK_PHPSPREADSHEET = -165; // 90 - 255
/**
* Horizontal alignment.
*
* @var string
*/
protected $horizontal = self::HORIZONTAL_GENERAL;
/**
* Vertical alignment.
*
* @var string
*/
protected $vertical = self::VERTICAL_BOTTOM;
/**
* Text rotation.
*
* @var int
*/
protected $textRotation = 0;
/**
* Wrap text.
*
* @var bool
*/
protected $wrapText = false;
/**
* Shrink to fit.
*
* @var bool
*/
protected $shrinkToFit = false;
/**
* Indent - only possible with horizontal alignment left and right.
*
* @var int
*/
protected $indent = 0;
/**
* Read order.
*
* @var int
*/
protected $readOrder = 0;
/**
* Create a new Alignment.
*
* @param bool $isSupervisor Flag indicating if this is a supervisor or not
* Leave this value at default unless you understand exactly what
* its ramifications are
* @param bool $isConditional Flag indicating if this is a conditional style or not
* Leave this value at default unless you understand exactly what
* its ramifications are
*/
public function __construct($isSupervisor = false, $isConditional = false)
{
// Supervisor?
parent::__construct($isSupervisor);
if ($isConditional) {
$this->horizontal = null;
$this->vertical = null;
$this->textRotation = null;
}
}
/**
* Get the shared style component for the currently active cell in currently active sheet.
* Only used for style supervisor.
*
* @return Alignment
*/
public function getSharedComponent()
{
return $this->parent->getSharedComponent()->getAlignment();
}
/**
* Build style array from subcomponents.
*
* @param array $array
*
* @return array
*/
public function getStyleArray($array)
{
return ['alignment' => $array];
}
/**
* Apply styles from array.
*
* <code>
* $spreadsheet->getActiveSheet()->getStyle('B2')->getAlignment()->applyFromArray(
* [
* 'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
* 'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
* 'textRotation' => 0,
* 'wrapText' => TRUE
* ]
* );
* </code>
*
* @param array $pStyles Array containing style information
*
* @return $this
*/
public function applyFromArray(array $pStyles)
{
if ($this->isSupervisor) {
$this->getActiveSheet()->getStyle($this->getSelectedCells())
->applyFromArray($this->getStyleArray($pStyles));
} else {
if (isset($pStyles['horizontal'])) {
$this->setHorizontal($pStyles['horizontal']);
}
if (isset($pStyles['vertical'])) {
$this->setVertical($pStyles['vertical']);
}
if (isset($pStyles['textRotation'])) {
$this->setTextRotation($pStyles['textRotation']);
}
if (isset($pStyles['wrapText'])) {
$this->setWrapText($pStyles['wrapText']);
}
if (isset($pStyles['shrinkToFit'])) {
$this->setShrinkToFit($pStyles['shrinkToFit']);
}
if (isset($pStyles['indent'])) {
$this->setIndent($pStyles['indent']);
}
if (isset($pStyles['readOrder'])) {
$this->setReadOrder($pStyles['readOrder']);
}
}
return $this;
}
/**
* Get Horizontal.
*
* @return string
*/
public function getHorizontal()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getHorizontal();
}
return $this->horizontal;
}
/**
* Set Horizontal.
*
* @param string $pValue see self::HORIZONTAL_*
*
* @return $this
*/
public function setHorizontal($pValue)
{
if ($pValue == '') {
$pValue = self::HORIZONTAL_GENERAL;
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['horizontal' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->horizontal = $pValue;
}
return $this;
}
/**
* Get Vertical.
*
* @return string
*/
public function getVertical()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getVertical();
}
return $this->vertical;
}
/**
* Set Vertical.
*
* @param string $pValue see self::VERTICAL_*
*
* @return $this
*/
public function setVertical($pValue)
{
if ($pValue == '') {
$pValue = self::VERTICAL_BOTTOM;
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['vertical' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->vertical = $pValue;
}
return $this;
}
/**
* Get TextRotation.
*
* @return int
*/
public function getTextRotation()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getTextRotation();
}
return $this->textRotation;
}
/**
* Set TextRotation.
*
* @param int $pValue
*
* @return $this
*/
public function setTextRotation($pValue)
{
// Excel2007 value 255 => PhpSpreadsheet value -165
if ($pValue == self::TEXTROTATION_STACK_EXCEL) {
$pValue = self::TEXTROTATION_STACK_PHPSPREADSHEET;
}
// Set rotation
if (($pValue >= -90 && $pValue <= 90) || $pValue == self::TEXTROTATION_STACK_PHPSPREADSHEET) {
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['textRotation' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->textRotation = $pValue;
}
} else {
throw new PhpSpreadsheetException('Text rotation should be a value between -90 and 90.');
}
return $this;
}
/**
* Get Wrap Text.
*
* @return bool
*/
public function getWrapText()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getWrapText();
}
return $this->wrapText;
}
/**
* Set Wrap Text.
*
* @param bool $pValue
*
* @return $this
*/
public function setWrapText($pValue)
{
if ($pValue == '') {
$pValue = false;
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['wrapText' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->wrapText = $pValue;
}
return $this;
}
/**
* Get Shrink to fit.
*
* @return bool
*/
public function getShrinkToFit()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getShrinkToFit();
}
return $this->shrinkToFit;
}
/**
* Set Shrink to fit.
*
* @param bool $pValue
*
* @return $this
*/
public function setShrinkToFit($pValue)
{
if ($pValue == '') {
$pValue = false;
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['shrinkToFit' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->shrinkToFit = $pValue;
}
return $this;
}
/**
* Get indent.
*
* @return int
*/
public function getIndent()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getIndent();
}
return $this->indent;
}
/**
* Set indent.
*
* @param int $pValue
*
* @return $this
*/
public function setIndent($pValue)
{
if ($pValue > 0) {
if (
$this->getHorizontal() != self::HORIZONTAL_GENERAL &&
$this->getHorizontal() != self::HORIZONTAL_LEFT &&
$this->getHorizontal() != self::HORIZONTAL_RIGHT
) {
$pValue = 0; // indent not supported
}
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['indent' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->indent = $pValue;
}
return $this;
}
/**
* Get read order.
*
* @return int
*/
public function getReadOrder()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getReadOrder();
}
return $this->readOrder;
}
/**
* Set read order.
*
* @param int $pValue
*
* @return $this
*/
public function setReadOrder($pValue)
{
if ($pValue < 0 || $pValue > 2) {
$pValue = 0;
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['readOrder' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->readOrder = $pValue;
}
return $this;
}
/**
* Get hash code.
*
* @return string Hash code
*/
public function getHashCode()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getHashCode();
}
return md5(
$this->horizontal .
$this->vertical .
$this->textRotation .
($this->wrapText ? 't' : 'f') .
($this->shrinkToFit ? 't' : 'f') .
$this->indent .
$this->readOrder .
__CLASS__
);
}
protected function exportArray1(): array
{
$exportedArray = [];
$this->exportArray2($exportedArray, 'horizontal', $this->getHorizontal());
$this->exportArray2($exportedArray, 'indent', $this->getIndent());
$this->exportArray2($exportedArray, 'readOrder', $this->getReadOrder());
$this->exportArray2($exportedArray, 'shrinkToFit', $this->getShrinkToFit());
$this->exportArray2($exportedArray, 'textRotation', $this->getTextRotation());
$this->exportArray2($exportedArray, 'vertical', $this->getVertical());
$this->exportArray2($exportedArray, 'wrapText', $this->getWrapText());
return $exportedArray;
}
}

View File

@@ -0,0 +1,234 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Style;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
class Border extends Supervisor
{
// Border style
const BORDER_NONE = 'none';
const BORDER_DASHDOT = 'dashDot';
const BORDER_DASHDOTDOT = 'dashDotDot';
const BORDER_DASHED = 'dashed';
const BORDER_DOTTED = 'dotted';
const BORDER_DOUBLE = 'double';
const BORDER_HAIR = 'hair';
const BORDER_MEDIUM = 'medium';
const BORDER_MEDIUMDASHDOT = 'mediumDashDot';
const BORDER_MEDIUMDASHDOTDOT = 'mediumDashDotDot';
const BORDER_MEDIUMDASHED = 'mediumDashed';
const BORDER_SLANTDASHDOT = 'slantDashDot';
const BORDER_THICK = 'thick';
const BORDER_THIN = 'thin';
/**
* Border style.
*
* @var string
*/
protected $borderStyle = self::BORDER_NONE;
/**
* Border color.
*
* @var Color
*/
protected $color;
/**
* @var int
*/
public $colorIndex;
/**
* Create a new Border.
*
* @param bool $isSupervisor Flag indicating if this is a supervisor or not
* Leave this value at default unless you understand exactly what
* its ramifications are
* @param bool $isConditional Flag indicating if this is a conditional style or not
* Leave this value at default unless you understand exactly what
* its ramifications are
*/
public function __construct($isSupervisor = false, $isConditional = false)
{
// Supervisor?
parent::__construct($isSupervisor);
// Initialise values
$this->color = new Color(Color::COLOR_BLACK, $isSupervisor);
// bind parent if we are a supervisor
if ($isSupervisor) {
$this->color->bindParent($this, 'color');
}
}
/**
* Get the shared style component for the currently active cell in currently active sheet.
* Only used for style supervisor.
*
* @return Border
*/
public function getSharedComponent()
{
switch ($this->parentPropertyName) {
case 'bottom':
return $this->parent->getSharedComponent()->getBottom();
case 'diagonal':
return $this->parent->getSharedComponent()->getDiagonal();
case 'left':
return $this->parent->getSharedComponent()->getLeft();
case 'right':
return $this->parent->getSharedComponent()->getRight();
case 'top':
return $this->parent->getSharedComponent()->getTop();
}
throw new PhpSpreadsheetException('Cannot get shared component for a pseudo-border.');
}
/**
* Build style array from subcomponents.
*
* @param array $array
*
* @return array
*/
public function getStyleArray($array)
{
return $this->parent->getStyleArray([$this->parentPropertyName => $array]);
}
/**
* Apply styles from array.
*
* <code>
* $spreadsheet->getActiveSheet()->getStyle('B2')->getBorders()->getTop()->applyFromArray(
* [
* 'borderStyle' => Border::BORDER_DASHDOT,
* 'color' => [
* 'rgb' => '808080'
* ]
* ]
* );
* </code>
*
* @param array $pStyles Array containing style information
*
* @return $this
*/
public function applyFromArray(array $pStyles)
{
if ($this->isSupervisor) {
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
} else {
if (isset($pStyles['borderStyle'])) {
$this->setBorderStyle($pStyles['borderStyle']);
}
if (isset($pStyles['color'])) {
$this->getColor()->applyFromArray($pStyles['color']);
}
}
return $this;
}
/**
* Get Border style.
*
* @return string
*/
public function getBorderStyle()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getBorderStyle();
}
return $this->borderStyle;
}
/**
* Set Border style.
*
* @param bool|string $pValue
* When passing a boolean, FALSE equates Border::BORDER_NONE
* and TRUE to Border::BORDER_MEDIUM
*
* @return $this
*/
public function setBorderStyle($pValue)
{
if (empty($pValue)) {
$pValue = self::BORDER_NONE;
} elseif (is_bool($pValue) && $pValue) {
$pValue = self::BORDER_MEDIUM;
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['borderStyle' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->borderStyle = $pValue;
}
return $this;
}
/**
* Get Border Color.
*
* @return Color
*/
public function getColor()
{
return $this->color;
}
/**
* Set Border Color.
*
* @return $this
*/
public function setColor(Color $pValue)
{
// make sure parameter is a real color and not a supervisor
$color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
if ($this->isSupervisor) {
$styleArray = $this->getColor()->getStyleArray(['argb' => $color->getARGB()]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->color = $color;
}
return $this;
}
/**
* Get hash code.
*
* @return string Hash code
*/
public function getHashCode()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getHashCode();
}
return md5(
$this->borderStyle .
$this->color->getHashCode() .
__CLASS__
);
}
protected function exportArray1(): array
{
$exportedArray = [];
$this->exportArray2($exportedArray, 'borderStyle', $this->getBorderStyle());
$this->exportArray2($exportedArray, 'color', $this->getColor());
return $exportedArray;
}
}

View File

@@ -0,0 +1,424 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Style;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
class Borders extends Supervisor
{
// Diagonal directions
const DIAGONAL_NONE = 0;
const DIAGONAL_UP = 1;
const DIAGONAL_DOWN = 2;
const DIAGONAL_BOTH = 3;
/**
* Left.
*
* @var Border
*/
protected $left;
/**
* Right.
*
* @var Border
*/
protected $right;
/**
* Top.
*
* @var Border
*/
protected $top;
/**
* Bottom.
*
* @var Border
*/
protected $bottom;
/**
* Diagonal.
*
* @var Border
*/
protected $diagonal;
/**
* DiagonalDirection.
*
* @var int
*/
protected $diagonalDirection;
/**
* All borders pseudo-border. Only applies to supervisor.
*
* @var Border
*/
protected $allBorders;
/**
* Outline pseudo-border. Only applies to supervisor.
*
* @var Border
*/
protected $outline;
/**
* Inside pseudo-border. Only applies to supervisor.
*
* @var Border
*/
protected $inside;
/**
* Vertical pseudo-border. Only applies to supervisor.
*
* @var Border
*/
protected $vertical;
/**
* Horizontal pseudo-border. Only applies to supervisor.
*
* @var Border
*/
protected $horizontal;
/**
* Create a new Borders.
*
* @param bool $isSupervisor Flag indicating if this is a supervisor or not
* Leave this value at default unless you understand exactly what
* its ramifications are
* @param bool $isConditional Flag indicating if this is a conditional style or not
* Leave this value at default unless you understand exactly what
* its ramifications are
*/
public function __construct($isSupervisor = false, $isConditional = false)
{
// Supervisor?
parent::__construct($isSupervisor);
// Initialise values
$this->left = new Border($isSupervisor, $isConditional);
$this->right = new Border($isSupervisor, $isConditional);
$this->top = new Border($isSupervisor, $isConditional);
$this->bottom = new Border($isSupervisor, $isConditional);
$this->diagonal = new Border($isSupervisor, $isConditional);
$this->diagonalDirection = self::DIAGONAL_NONE;
// Specially for supervisor
if ($isSupervisor) {
// Initialize pseudo-borders
$this->allBorders = new Border(true);
$this->outline = new Border(true);
$this->inside = new Border(true);
$this->vertical = new Border(true);
$this->horizontal = new Border(true);
// bind parent if we are a supervisor
$this->left->bindParent($this, 'left');
$this->right->bindParent($this, 'right');
$this->top->bindParent($this, 'top');
$this->bottom->bindParent($this, 'bottom');
$this->diagonal->bindParent($this, 'diagonal');
$this->allBorders->bindParent($this, 'allBorders');
$this->outline->bindParent($this, 'outline');
$this->inside->bindParent($this, 'inside');
$this->vertical->bindParent($this, 'vertical');
$this->horizontal->bindParent($this, 'horizontal');
}
}
/**
* Get the shared style component for the currently active cell in currently active sheet.
* Only used for style supervisor.
*
* @return Borders
*/
public function getSharedComponent()
{
return $this->parent->getSharedComponent()->getBorders();
}
/**
* Build style array from subcomponents.
*
* @param array $array
*
* @return array
*/
public function getStyleArray($array)
{
return ['borders' => $array];
}
/**
* Apply styles from array.
*
* <code>
* $spreadsheet->getActiveSheet()->getStyle('B2')->getBorders()->applyFromArray(
* [
* 'bottom' => [
* 'borderStyle' => Border::BORDER_DASHDOT,
* 'color' => [
* 'rgb' => '808080'
* ]
* ],
* 'top' => [
* 'borderStyle' => Border::BORDER_DASHDOT,
* 'color' => [
* 'rgb' => '808080'
* ]
* ]
* ]
* );
* </code>
*
* <code>
* $spreadsheet->getActiveSheet()->getStyle('B2')->getBorders()->applyFromArray(
* [
* 'allBorders' => [
* 'borderStyle' => Border::BORDER_DASHDOT,
* 'color' => [
* 'rgb' => '808080'
* ]
* ]
* ]
* );
* </code>
*
* @param array $pStyles Array containing style information
*
* @return $this
*/
public function applyFromArray(array $pStyles)
{
if ($this->isSupervisor) {
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
} else {
if (isset($pStyles['left'])) {
$this->getLeft()->applyFromArray($pStyles['left']);
}
if (isset($pStyles['right'])) {
$this->getRight()->applyFromArray($pStyles['right']);
}
if (isset($pStyles['top'])) {
$this->getTop()->applyFromArray($pStyles['top']);
}
if (isset($pStyles['bottom'])) {
$this->getBottom()->applyFromArray($pStyles['bottom']);
}
if (isset($pStyles['diagonal'])) {
$this->getDiagonal()->applyFromArray($pStyles['diagonal']);
}
if (isset($pStyles['diagonalDirection'])) {
$this->setDiagonalDirection($pStyles['diagonalDirection']);
}
if (isset($pStyles['allBorders'])) {
$this->getLeft()->applyFromArray($pStyles['allBorders']);
$this->getRight()->applyFromArray($pStyles['allBorders']);
$this->getTop()->applyFromArray($pStyles['allBorders']);
$this->getBottom()->applyFromArray($pStyles['allBorders']);
}
}
return $this;
}
/**
* Get Left.
*
* @return Border
*/
public function getLeft()
{
return $this->left;
}
/**
* Get Right.
*
* @return Border
*/
public function getRight()
{
return $this->right;
}
/**
* Get Top.
*
* @return Border
*/
public function getTop()
{
return $this->top;
}
/**
* Get Bottom.
*
* @return Border
*/
public function getBottom()
{
return $this->bottom;
}
/**
* Get Diagonal.
*
* @return Border
*/
public function getDiagonal()
{
return $this->diagonal;
}
/**
* Get AllBorders (pseudo-border). Only applies to supervisor.
*
* @return Border
*/
public function getAllBorders()
{
if (!$this->isSupervisor) {
throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
}
return $this->allBorders;
}
/**
* Get Outline (pseudo-border). Only applies to supervisor.
*
* @return Border
*/
public function getOutline()
{
if (!$this->isSupervisor) {
throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
}
return $this->outline;
}
/**
* Get Inside (pseudo-border). Only applies to supervisor.
*
* @return Border
*/
public function getInside()
{
if (!$this->isSupervisor) {
throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
}
return $this->inside;
}
/**
* Get Vertical (pseudo-border). Only applies to supervisor.
*
* @return Border
*/
public function getVertical()
{
if (!$this->isSupervisor) {
throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
}
return $this->vertical;
}
/**
* Get Horizontal (pseudo-border). Only applies to supervisor.
*
* @return Border
*/
public function getHorizontal()
{
if (!$this->isSupervisor) {
throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
}
return $this->horizontal;
}
/**
* Get DiagonalDirection.
*
* @return int
*/
public function getDiagonalDirection()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getDiagonalDirection();
}
return $this->diagonalDirection;
}
/**
* Set DiagonalDirection.
*
* @param int $pValue see self::DIAGONAL_*
*
* @return $this
*/
public function setDiagonalDirection($pValue)
{
if ($pValue == '') {
$pValue = self::DIAGONAL_NONE;
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['diagonalDirection' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->diagonalDirection = $pValue;
}
return $this;
}
/**
* Get hash code.
*
* @return string Hash code
*/
public function getHashCode()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getHashcode();
}
return md5(
$this->getLeft()->getHashCode() .
$this->getRight()->getHashCode() .
$this->getTop()->getHashCode() .
$this->getBottom()->getHashCode() .
$this->getDiagonal()->getHashCode() .
$this->getDiagonalDirection() .
__CLASS__
);
}
protected function exportArray1(): array
{
$exportedArray = [];
$this->exportArray2($exportedArray, 'bottom', $this->getBottom());
$this->exportArray2($exportedArray, 'diagonal', $this->getDiagonal());
$this->exportArray2($exportedArray, 'diagonalDirection', $this->getDiagonalDirection());
$this->exportArray2($exportedArray, 'left', $this->getLeft());
$this->exportArray2($exportedArray, 'right', $this->getRight());
$this->exportArray2($exportedArray, 'top', $this->getTop());
return $exportedArray;
}
}

View File

@@ -0,0 +1,400 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Style;
class Color extends Supervisor
{
const NAMED_COLORS = [
'Black',
'White',
'Red',
'Green',
'Blue',
'Yellow',
'Magenta',
'Cyan',
];
// Colors
const COLOR_BLACK = 'FF000000';
const COLOR_WHITE = 'FFFFFFFF';
const COLOR_RED = 'FFFF0000';
const COLOR_DARKRED = 'FF800000';
const COLOR_BLUE = 'FF0000FF';
const COLOR_DARKBLUE = 'FF000080';
const COLOR_GREEN = 'FF00FF00';
const COLOR_DARKGREEN = 'FF008000';
const COLOR_YELLOW = 'FFFFFF00';
const COLOR_DARKYELLOW = 'FF808000';
/**
* Indexed colors array.
*
* @var array
*/
protected static $indexedColors;
/**
* ARGB - Alpha RGB.
*
* @var string
*/
protected $argb;
/**
* Create a new Color.
*
* @param string $pARGB ARGB value for the colour
* @param bool $isSupervisor Flag indicating if this is a supervisor or not
* Leave this value at default unless you understand exactly what
* its ramifications are
* @param bool $isConditional Flag indicating if this is a conditional style or not
* Leave this value at default unless you understand exactly what
* its ramifications are
*/
public function __construct($pARGB = self::COLOR_BLACK, $isSupervisor = false, $isConditional = false)
{
// Supervisor?
parent::__construct($isSupervisor);
// Initialise values
if (!$isConditional) {
$this->argb = $pARGB;
}
}
/**
* Get the shared style component for the currently active cell in currently active sheet.
* Only used for style supervisor.
*
* @return Color
*/
public function getSharedComponent()
{
if ($this->parentPropertyName === 'endColor') {
return $this->parent->getSharedComponent()->getEndColor();
}
if ($this->parentPropertyName === 'startColor') {
return $this->parent->getSharedComponent()->getStartColor();
}
return $this->parent->getSharedComponent()->getColor();
}
/**
* Build style array from subcomponents.
*
* @param array $array
*
* @return array
*/
public function getStyleArray($array)
{
return $this->parent->getStyleArray([$this->parentPropertyName => $array]);
}
/**
* Apply styles from array.
*
* <code>
* $spreadsheet->getActiveSheet()->getStyle('B2')->getFont()->getColor()->applyFromArray(['rgb' => '808080']);
* </code>
*
* @param array $pStyles Array containing style information
*
* @return $this
*/
public function applyFromArray(array $pStyles)
{
if ($this->isSupervisor) {
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
} else {
if (isset($pStyles['rgb'])) {
$this->setRGB($pStyles['rgb']);
}
if (isset($pStyles['argb'])) {
$this->setARGB($pStyles['argb']);
}
}
return $this;
}
/**
* Get ARGB.
*
* @return string
*/
public function getARGB()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getARGB();
}
return $this->argb;
}
/**
* Set ARGB.
*
* @param string $pValue see self::COLOR_*
*
* @return $this
*/
public function setARGB($pValue)
{
if ($pValue == '') {
$pValue = self::COLOR_BLACK;
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['argb' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->argb = $pValue;
}
return $this;
}
/**
* Get RGB.
*
* @return string
*/
public function getRGB()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getRGB();
}
return substr($this->argb, 2);
}
/**
* Set RGB.
*
* @param string $pValue RGB value
*
* @return $this
*/
public function setRGB($pValue)
{
if ($pValue == '') {
$pValue = '000000';
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['argb' => 'FF' . $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->argb = 'FF' . $pValue;
}
return $this;
}
/**
* Get a specified colour component of an RGB value.
*
* @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
* @param int $offset Position within the RGB value to extract
* @param bool $hex Flag indicating whether the component should be returned as a hex or a
* decimal value
*
* @return string The extracted colour component
*/
private static function getColourComponent($RGB, $offset, $hex = true)
{
$colour = substr($RGB, $offset, 2);
return ($hex) ? $colour : hexdec($colour);
}
/**
* Get the red colour component of an RGB value.
*
* @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
* @param bool $hex Flag indicating whether the component should be returned as a hex or a
* decimal value
*
* @return string The red colour component
*/
public static function getRed($RGB, $hex = true)
{
return self::getColourComponent($RGB, strlen($RGB) - 6, $hex);
}
/**
* Get the green colour component of an RGB value.
*
* @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
* @param bool $hex Flag indicating whether the component should be returned as a hex or a
* decimal value
*
* @return string The green colour component
*/
public static function getGreen($RGB, $hex = true)
{
return self::getColourComponent($RGB, strlen($RGB) - 4, $hex);
}
/**
* Get the blue colour component of an RGB value.
*
* @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
* @param bool $hex Flag indicating whether the component should be returned as a hex or a
* decimal value
*
* @return string The blue colour component
*/
public static function getBlue($RGB, $hex = true)
{
return self::getColourComponent($RGB, strlen($RGB) - 2, $hex);
}
/**
* Adjust the brightness of a color.
*
* @param string $hex The colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
* @param float $adjustPercentage The percentage by which to adjust the colour as a float from -1 to 1
*
* @return string The adjusted colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
*/
public static function changeBrightness($hex, $adjustPercentage)
{
$rgba = (strlen($hex) === 8);
$adjustPercentage = max(-1.0, min(1.0, $adjustPercentage));
$red = self::getRed($hex, false);
$green = self::getGreen($hex, false);
$blue = self::getBlue($hex, false);
if ($adjustPercentage > 0) {
$red += (255 - $red) * $adjustPercentage;
$green += (255 - $green) * $adjustPercentage;
$blue += (255 - $blue) * $adjustPercentage;
} else {
$red += $red * $adjustPercentage;
$green += $green * $adjustPercentage;
$blue += $blue * $adjustPercentage;
}
$rgb = strtoupper(
str_pad(dechex((int) $red), 2, '0', 0) .
str_pad(dechex((int) $green), 2, '0', 0) .
str_pad(dechex((int) $blue), 2, '0', 0)
);
return (($rgba) ? 'FF' : '') . $rgb;
}
/**
* Get indexed color.
*
* @param int $pIndex Index entry point into the colour array
* @param bool $background Flag to indicate whether default background or foreground colour
* should be returned if the indexed colour doesn't exist
*
* @return self
*/
public static function indexedColor($pIndex, $background = false)
{
// Clean parameter
$pIndex = (int) $pIndex;
// Indexed colors
if (self::$indexedColors === null) {
self::$indexedColors = [
1 => 'FF000000', // System Colour #1 - Black
2 => 'FFFFFFFF', // System Colour #2 - White
3 => 'FFFF0000', // System Colour #3 - Red
4 => 'FF00FF00', // System Colour #4 - Green
5 => 'FF0000FF', // System Colour #5 - Blue
6 => 'FFFFFF00', // System Colour #6 - Yellow
7 => 'FFFF00FF', // System Colour #7- Magenta
8 => 'FF00FFFF', // System Colour #8- Cyan
9 => 'FF800000', // Standard Colour #9
10 => 'FF008000', // Standard Colour #10
11 => 'FF000080', // Standard Colour #11
12 => 'FF808000', // Standard Colour #12
13 => 'FF800080', // Standard Colour #13
14 => 'FF008080', // Standard Colour #14
15 => 'FFC0C0C0', // Standard Colour #15
16 => 'FF808080', // Standard Colour #16
17 => 'FF9999FF', // Chart Fill Colour #17
18 => 'FF993366', // Chart Fill Colour #18
19 => 'FFFFFFCC', // Chart Fill Colour #19
20 => 'FFCCFFFF', // Chart Fill Colour #20
21 => 'FF660066', // Chart Fill Colour #21
22 => 'FFFF8080', // Chart Fill Colour #22
23 => 'FF0066CC', // Chart Fill Colour #23
24 => 'FFCCCCFF', // Chart Fill Colour #24
25 => 'FF000080', // Chart Line Colour #25
26 => 'FFFF00FF', // Chart Line Colour #26
27 => 'FFFFFF00', // Chart Line Colour #27
28 => 'FF00FFFF', // Chart Line Colour #28
29 => 'FF800080', // Chart Line Colour #29
30 => 'FF800000', // Chart Line Colour #30
31 => 'FF008080', // Chart Line Colour #31
32 => 'FF0000FF', // Chart Line Colour #32
33 => 'FF00CCFF', // Standard Colour #33
34 => 'FFCCFFFF', // Standard Colour #34
35 => 'FFCCFFCC', // Standard Colour #35
36 => 'FFFFFF99', // Standard Colour #36
37 => 'FF99CCFF', // Standard Colour #37
38 => 'FFFF99CC', // Standard Colour #38
39 => 'FFCC99FF', // Standard Colour #39
40 => 'FFFFCC99', // Standard Colour #40
41 => 'FF3366FF', // Standard Colour #41
42 => 'FF33CCCC', // Standard Colour #42
43 => 'FF99CC00', // Standard Colour #43
44 => 'FFFFCC00', // Standard Colour #44
45 => 'FFFF9900', // Standard Colour #45
46 => 'FFFF6600', // Standard Colour #46
47 => 'FF666699', // Standard Colour #47
48 => 'FF969696', // Standard Colour #48
49 => 'FF003366', // Standard Colour #49
50 => 'FF339966', // Standard Colour #50
51 => 'FF003300', // Standard Colour #51
52 => 'FF333300', // Standard Colour #52
53 => 'FF993300', // Standard Colour #53
54 => 'FF993366', // Standard Colour #54
55 => 'FF333399', // Standard Colour #55
56 => 'FF333333', // Standard Colour #56
];
}
if (isset(self::$indexedColors[$pIndex])) {
return new self(self::$indexedColors[$pIndex]);
}
if ($background) {
return new self(self::COLOR_WHITE);
}
return new self(self::COLOR_BLACK);
}
/**
* Get hash code.
*
* @return string Hash code
*/
public function getHashCode()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getHashCode();
}
return md5(
$this->argb .
__CLASS__
);
}
protected function exportArray1(): array
{
$exportedArray = [];
$this->exportArray2($exportedArray, 'argb', $this->getARGB());
return $exportedArray;
}
}

View File

@@ -0,0 +1,274 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Style;
use PhpOffice\PhpSpreadsheet\IComparable;
class Conditional implements IComparable
{
// Condition types
const CONDITION_NONE = 'none';
const CONDITION_CELLIS = 'cellIs';
const CONDITION_CONTAINSTEXT = 'containsText';
const CONDITION_EXPRESSION = 'expression';
const CONDITION_CONTAINSBLANKS = 'containsBlanks';
const CONDITION_NOTCONTAINSBLANKS = 'notContainsBlanks';
// Operator types
const OPERATOR_NONE = '';
const OPERATOR_BEGINSWITH = 'beginsWith';
const OPERATOR_ENDSWITH = 'endsWith';
const OPERATOR_EQUAL = 'equal';
const OPERATOR_GREATERTHAN = 'greaterThan';
const OPERATOR_GREATERTHANOREQUAL = 'greaterThanOrEqual';
const OPERATOR_LESSTHAN = 'lessThan';
const OPERATOR_LESSTHANOREQUAL = 'lessThanOrEqual';
const OPERATOR_NOTEQUAL = 'notEqual';
const OPERATOR_CONTAINSTEXT = 'containsText';
const OPERATOR_NOTCONTAINS = 'notContains';
const OPERATOR_BETWEEN = 'between';
const OPERATOR_NOTBETWEEN = 'notBetween';
/**
* Condition type.
*
* @var string
*/
private $conditionType = self::CONDITION_NONE;
/**
* Operator type.
*
* @var string
*/
private $operatorType = self::OPERATOR_NONE;
/**
* Text.
*
* @var string
*/
private $text;
/**
* Stop on this condition, if it matches.
*
* @var bool
*/
private $stopIfTrue = false;
/**
* Condition.
*
* @var string[]
*/
private $condition = [];
/**
* Style.
*
* @var Style
*/
private $style;
/**
* Create a new Conditional.
*/
public function __construct()
{
// Initialise values
$this->style = new Style(false, true);
}
/**
* Get Condition type.
*
* @return string
*/
public function getConditionType()
{
return $this->conditionType;
}
/**
* Set Condition type.
*
* @param string $pValue Condition type, see self::CONDITION_*
*
* @return $this
*/
public function setConditionType($pValue)
{
$this->conditionType = $pValue;
return $this;
}
/**
* Get Operator type.
*
* @return string
*/
public function getOperatorType()
{
return $this->operatorType;
}
/**
* Set Operator type.
*
* @param string $pValue Conditional operator type, see self::OPERATOR_*
*
* @return $this
*/
public function setOperatorType($pValue)
{
$this->operatorType = $pValue;
return $this;
}
/**
* Get text.
*
* @return string
*/
public function getText()
{
return $this->text;
}
/**
* Set text.
*
* @param string $value
*
* @return $this
*/
public function setText($value)
{
$this->text = $value;
return $this;
}
/**
* Get StopIfTrue.
*
* @return bool
*/
public function getStopIfTrue()
{
return $this->stopIfTrue;
}
/**
* Set StopIfTrue.
*
* @param bool $value
*
* @return $this
*/
public function setStopIfTrue($value)
{
$this->stopIfTrue = $value;
return $this;
}
/**
* Get Conditions.
*
* @return string[]
*/
public function getConditions()
{
return $this->condition;
}
/**
* Set Conditions.
*
* @param bool|float|int|string|string[] $pValue Condition
*
* @return $this
*/
public function setConditions($pValue)
{
if (!is_array($pValue)) {
$pValue = [$pValue];
}
$this->condition = $pValue;
return $this;
}
/**
* Add Condition.
*
* @param string $pValue Condition
*
* @return $this
*/
public function addCondition($pValue)
{
$this->condition[] = $pValue;
return $this;
}
/**
* Get Style.
*
* @return Style
*/
public function getStyle()
{
return $this->style;
}
/**
* Set Style.
*
* @param Style $pValue
*
* @return $this
*/
public function setStyle(?Style $pValue = null)
{
$this->style = $pValue;
return $this;
}
/**
* Get hash code.
*
* @return string Hash code
*/
public function getHashCode()
{
return md5(
$this->conditionType .
$this->operatorType .
implode(';', $this->condition) .
$this->style->getHashCode() .
__CLASS__
);
}
/**
* Implement PHP __clone to create a deep clone, not just a shallow copy.
*/
public function __clone()
{
$vars = get_object_vars($this);
foreach ($vars as $key => $value) {
if (is_object($value)) {
$this->$key = clone $value;
} else {
$this->$key = $value;
}
}
}
}

View File

@@ -0,0 +1,325 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Style;
class Fill extends Supervisor
{
// Fill types
const FILL_NONE = 'none';
const FILL_SOLID = 'solid';
const FILL_GRADIENT_LINEAR = 'linear';
const FILL_GRADIENT_PATH = 'path';
const FILL_PATTERN_DARKDOWN = 'darkDown';
const FILL_PATTERN_DARKGRAY = 'darkGray';
const FILL_PATTERN_DARKGRID = 'darkGrid';
const FILL_PATTERN_DARKHORIZONTAL = 'darkHorizontal';
const FILL_PATTERN_DARKTRELLIS = 'darkTrellis';
const FILL_PATTERN_DARKUP = 'darkUp';
const FILL_PATTERN_DARKVERTICAL = 'darkVertical';
const FILL_PATTERN_GRAY0625 = 'gray0625';
const FILL_PATTERN_GRAY125 = 'gray125';
const FILL_PATTERN_LIGHTDOWN = 'lightDown';
const FILL_PATTERN_LIGHTGRAY = 'lightGray';
const FILL_PATTERN_LIGHTGRID = 'lightGrid';
const FILL_PATTERN_LIGHTHORIZONTAL = 'lightHorizontal';
const FILL_PATTERN_LIGHTTRELLIS = 'lightTrellis';
const FILL_PATTERN_LIGHTUP = 'lightUp';
const FILL_PATTERN_LIGHTVERTICAL = 'lightVertical';
const FILL_PATTERN_MEDIUMGRAY = 'mediumGray';
/**
* @var int
*/
public $startcolorIndex;
/**
* @var int
*/
public $endcolorIndex;
/**
* Fill type.
*
* @var string
*/
protected $fillType = self::FILL_NONE;
/**
* Rotation.
*
* @var float
*/
protected $rotation = 0;
/**
* Start color.
*
* @var Color
*/
protected $startColor;
/**
* End color.
*
* @var Color
*/
protected $endColor;
/**
* Create a new Fill.
*
* @param bool $isSupervisor Flag indicating if this is a supervisor or not
* Leave this value at default unless you understand exactly what
* its ramifications are
* @param bool $isConditional Flag indicating if this is a conditional style or not
* Leave this value at default unless you understand exactly what
* its ramifications are
*/
public function __construct($isSupervisor = false, $isConditional = false)
{
// Supervisor?
parent::__construct($isSupervisor);
// Initialise values
if ($isConditional) {
$this->fillType = null;
}
$this->startColor = new Color(Color::COLOR_WHITE, $isSupervisor, $isConditional);
$this->endColor = new Color(Color::COLOR_BLACK, $isSupervisor, $isConditional);
// bind parent if we are a supervisor
if ($isSupervisor) {
$this->startColor->bindParent($this, 'startColor');
$this->endColor->bindParent($this, 'endColor');
}
}
/**
* Get the shared style component for the currently active cell in currently active sheet.
* Only used for style supervisor.
*
* @return Fill
*/
public function getSharedComponent()
{
return $this->parent->getSharedComponent()->getFill();
}
/**
* Build style array from subcomponents.
*
* @param array $array
*
* @return array
*/
public function getStyleArray($array)
{
return ['fill' => $array];
}
/**
* Apply styles from array.
*
* <code>
* $spreadsheet->getActiveSheet()->getStyle('B2')->getFill()->applyFromArray(
* [
* 'fillType' => Fill::FILL_GRADIENT_LINEAR,
* 'rotation' => 0,
* 'startColor' => [
* 'rgb' => '000000'
* ],
* 'endColor' => [
* 'argb' => 'FFFFFFFF'
* ]
* ]
* );
* </code>
*
* @param array $pStyles Array containing style information
*
* @return $this
*/
public function applyFromArray(array $pStyles)
{
if ($this->isSupervisor) {
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
} else {
if (isset($pStyles['fillType'])) {
$this->setFillType($pStyles['fillType']);
}
if (isset($pStyles['rotation'])) {
$this->setRotation($pStyles['rotation']);
}
if (isset($pStyles['startColor'])) {
$this->getStartColor()->applyFromArray($pStyles['startColor']);
}
if (isset($pStyles['endColor'])) {
$this->getEndColor()->applyFromArray($pStyles['endColor']);
}
if (isset($pStyles['color'])) {
$this->getStartColor()->applyFromArray($pStyles['color']);
$this->getEndColor()->applyFromArray($pStyles['color']);
}
}
return $this;
}
/**
* Get Fill Type.
*
* @return string
*/
public function getFillType()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getFillType();
}
return $this->fillType;
}
/**
* Set Fill Type.
*
* @param string $pValue Fill type, see self::FILL_*
*
* @return $this
*/
public function setFillType($pValue)
{
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['fillType' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->fillType = $pValue;
}
return $this;
}
/**
* Get Rotation.
*
* @return float
*/
public function getRotation()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getRotation();
}
return $this->rotation;
}
/**
* Set Rotation.
*
* @param float $pValue
*
* @return $this
*/
public function setRotation($pValue)
{
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['rotation' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->rotation = $pValue;
}
return $this;
}
/**
* Get Start Color.
*
* @return Color
*/
public function getStartColor()
{
return $this->startColor;
}
/**
* Set Start Color.
*
* @return $this
*/
public function setStartColor(Color $pValue)
{
// make sure parameter is a real color and not a supervisor
$color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
if ($this->isSupervisor) {
$styleArray = $this->getStartColor()->getStyleArray(['argb' => $color->getARGB()]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->startColor = $color;
}
return $this;
}
/**
* Get End Color.
*
* @return Color
*/
public function getEndColor()
{
return $this->endColor;
}
/**
* Set End Color.
*
* @return $this
*/
public function setEndColor(Color $pValue)
{
// make sure parameter is a real color and not a supervisor
$color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
if ($this->isSupervisor) {
$styleArray = $this->getEndColor()->getStyleArray(['argb' => $color->getARGB()]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->endColor = $color;
}
return $this;
}
/**
* Get hash code.
*
* @return string Hash code
*/
public function getHashCode()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getHashCode();
}
// Note that we don't care about colours for fill type NONE, but could have duplicate NONEs with
// different hashes if we don't explicitly prevent this
return md5(
$this->getFillType() .
$this->getRotation() .
($this->getFillType() !== self::FILL_NONE ? $this->getStartColor()->getHashCode() : '') .
($this->getFillType() !== self::FILL_NONE ? $this->getEndColor()->getHashCode() : '') .
__CLASS__
);
}
protected function exportArray1(): array
{
$exportedArray = [];
$this->exportArray2($exportedArray, 'endColor', $this->getEndColor());
$this->exportArray2($exportedArray, 'fillType', $this->getFillType());
$this->exportArray2($exportedArray, 'rotation', $this->getRotation());
$this->exportArray2($exportedArray, 'startColor', $this->getStartColor());
return $exportedArray;
}
}

View File

@@ -0,0 +1,558 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Style;
class Font extends Supervisor
{
// Underline types
const UNDERLINE_NONE = 'none';
const UNDERLINE_DOUBLE = 'double';
const UNDERLINE_DOUBLEACCOUNTING = 'doubleAccounting';
const UNDERLINE_SINGLE = 'single';
const UNDERLINE_SINGLEACCOUNTING = 'singleAccounting';
/**
* Font Name.
*
* @var string
*/
protected $name = 'Calibri';
/**
* Font Size.
*
* @var float
*/
protected $size = 11;
/**
* Bold.
*
* @var bool
*/
protected $bold = false;
/**
* Italic.
*
* @var bool
*/
protected $italic = false;
/**
* Superscript.
*
* @var bool
*/
protected $superscript = false;
/**
* Subscript.
*
* @var bool
*/
protected $subscript = false;
/**
* Underline.
*
* @var string
*/
protected $underline = self::UNDERLINE_NONE;
/**
* Strikethrough.
*
* @var bool
*/
protected $strikethrough = false;
/**
* Foreground color.
*
* @var Color
*/
protected $color;
/**
* @var int
*/
public $colorIndex;
/**
* Create a new Font.
*
* @param bool $isSupervisor Flag indicating if this is a supervisor or not
* Leave this value at default unless you understand exactly what
* its ramifications are
* @param bool $isConditional Flag indicating if this is a conditional style or not
* Leave this value at default unless you understand exactly what
* its ramifications are
*/
public function __construct($isSupervisor = false, $isConditional = false)
{
// Supervisor?
parent::__construct($isSupervisor);
// Initialise values
if ($isConditional) {
$this->name = null;
$this->size = null;
$this->bold = null;
$this->italic = null;
$this->superscript = null;
$this->subscript = null;
$this->underline = null;
$this->strikethrough = null;
$this->color = new Color(Color::COLOR_BLACK, $isSupervisor, $isConditional);
} else {
$this->color = new Color(Color::COLOR_BLACK, $isSupervisor);
}
// bind parent if we are a supervisor
if ($isSupervisor) {
$this->color->bindParent($this, 'color');
}
}
/**
* Get the shared style component for the currently active cell in currently active sheet.
* Only used for style supervisor.
*
* @return Font
*/
public function getSharedComponent()
{
return $this->parent->getSharedComponent()->getFont();
}
/**
* Build style array from subcomponents.
*
* @param array $array
*
* @return array
*/
public function getStyleArray($array)
{
return ['font' => $array];
}
/**
* Apply styles from array.
*
* <code>
* $spreadsheet->getActiveSheet()->getStyle('B2')->getFont()->applyFromArray(
* [
* 'name' => 'Arial',
* 'bold' => TRUE,
* 'italic' => FALSE,
* 'underline' => \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE,
* 'strikethrough' => FALSE,
* 'color' => [
* 'rgb' => '808080'
* ]
* ]
* );
* </code>
*
* @param array $pStyles Array containing style information
*
* @return $this
*/
public function applyFromArray(array $pStyles)
{
if ($this->isSupervisor) {
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
} else {
if (isset($pStyles['name'])) {
$this->setName($pStyles['name']);
}
if (isset($pStyles['bold'])) {
$this->setBold($pStyles['bold']);
}
if (isset($pStyles['italic'])) {
$this->setItalic($pStyles['italic']);
}
if (isset($pStyles['superscript'])) {
$this->setSuperscript($pStyles['superscript']);
}
if (isset($pStyles['subscript'])) {
$this->setSubscript($pStyles['subscript']);
}
if (isset($pStyles['underline'])) {
$this->setUnderline($pStyles['underline']);
}
if (isset($pStyles['strikethrough'])) {
$this->setStrikethrough($pStyles['strikethrough']);
}
if (isset($pStyles['color'])) {
$this->getColor()->applyFromArray($pStyles['color']);
}
if (isset($pStyles['size'])) {
$this->setSize($pStyles['size']);
}
}
return $this;
}
/**
* Get Name.
*
* @return string
*/
public function getName()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getName();
}
return $this->name;
}
/**
* Set Name.
*
* @param string $pValue
*
* @return $this
*/
public function setName($pValue)
{
if ($pValue == '') {
$pValue = 'Calibri';
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['name' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->name = $pValue;
}
return $this;
}
/**
* Get Size.
*
* @return float
*/
public function getSize()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getSize();
}
return $this->size;
}
/**
* Set Size.
*
* @param float $pValue
*
* @return $this
*/
public function setSize($pValue)
{
if ($pValue == '') {
$pValue = 10;
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['size' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->size = $pValue;
}
return $this;
}
/**
* Get Bold.
*
* @return bool
*/
public function getBold()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getBold();
}
return $this->bold;
}
/**
* Set Bold.
*
* @param bool $pValue
*
* @return $this
*/
public function setBold($pValue)
{
if ($pValue == '') {
$pValue = false;
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['bold' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->bold = $pValue;
}
return $this;
}
/**
* Get Italic.
*
* @return bool
*/
public function getItalic()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getItalic();
}
return $this->italic;
}
/**
* Set Italic.
*
* @param bool $pValue
*
* @return $this
*/
public function setItalic($pValue)
{
if ($pValue == '') {
$pValue = false;
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['italic' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->italic = $pValue;
}
return $this;
}
/**
* Get Superscript.
*
* @return bool
*/
public function getSuperscript()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getSuperscript();
}
return $this->superscript;
}
/**
* Set Superscript.
*
* @return $this
*/
public function setSuperscript(bool $pValue)
{
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['superscript' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->superscript = $pValue;
if ($this->superscript) {
$this->subscript = false;
}
}
return $this;
}
/**
* Get Subscript.
*
* @return bool
*/
public function getSubscript()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getSubscript();
}
return $this->subscript;
}
/**
* Set Subscript.
*
* @return $this
*/
public function setSubscript(bool $pValue)
{
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['subscript' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->subscript = $pValue;
if ($this->subscript) {
$this->superscript = false;
}
}
return $this;
}
/**
* Get Underline.
*
* @return string
*/
public function getUnderline()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getUnderline();
}
return $this->underline;
}
/**
* Set Underline.
*
* @param bool|string $pValue \PhpOffice\PhpSpreadsheet\Style\Font underline type
* If a boolean is passed, then TRUE equates to UNDERLINE_SINGLE,
* false equates to UNDERLINE_NONE
*
* @return $this
*/
public function setUnderline($pValue)
{
if (is_bool($pValue)) {
$pValue = ($pValue) ? self::UNDERLINE_SINGLE : self::UNDERLINE_NONE;
} elseif ($pValue == '') {
$pValue = self::UNDERLINE_NONE;
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['underline' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->underline = $pValue;
}
return $this;
}
/**
* Get Strikethrough.
*
* @return bool
*/
public function getStrikethrough()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getStrikethrough();
}
return $this->strikethrough;
}
/**
* Set Strikethrough.
*
* @param bool $pValue
*
* @return $this
*/
public function setStrikethrough($pValue)
{
if ($pValue == '') {
$pValue = false;
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['strikethrough' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->strikethrough = $pValue;
}
return $this;
}
/**
* Get Color.
*
* @return Color
*/
public function getColor()
{
return $this->color;
}
/**
* Set Color.
*
* @return $this
*/
public function setColor(Color $pValue)
{
// make sure parameter is a real color and not a supervisor
$color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
if ($this->isSupervisor) {
$styleArray = $this->getColor()->getStyleArray(['argb' => $color->getARGB()]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->color = $color;
}
return $this;
}
/**
* Get hash code.
*
* @return string Hash code
*/
public function getHashCode()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getHashCode();
}
return md5(
$this->name .
$this->size .
($this->bold ? 't' : 'f') .
($this->italic ? 't' : 'f') .
($this->superscript ? 't' : 'f') .
($this->subscript ? 't' : 'f') .
$this->underline .
($this->strikethrough ? 't' : 'f') .
$this->color->getHashCode() .
__CLASS__
);
}
protected function exportArray1(): array
{
$exportedArray = [];
$this->exportArray2($exportedArray, 'bold', $this->getBold());
$this->exportArray2($exportedArray, 'color', $this->getColor());
$this->exportArray2($exportedArray, 'italic', $this->getItalic());
$this->exportArray2($exportedArray, 'name', $this->getName());
$this->exportArray2($exportedArray, 'size', $this->getSize());
$this->exportArray2($exportedArray, 'strikethrough', $this->getStrikethrough());
$this->exportArray2($exportedArray, 'subscript', $this->getSubscript());
$this->exportArray2($exportedArray, 'superscript', $this->getSuperscript());
$this->exportArray2($exportedArray, 'underline', $this->getUnderline());
return $exportedArray;
}
}

View File

@@ -0,0 +1,881 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Style;
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
class NumberFormat extends Supervisor
{
// Pre-defined formats
const FORMAT_GENERAL = 'General';
const FORMAT_TEXT = '@';
const FORMAT_NUMBER = '0';
const FORMAT_NUMBER_00 = '0.00';
const FORMAT_NUMBER_COMMA_SEPARATED1 = '#,##0.00';
const FORMAT_NUMBER_COMMA_SEPARATED2 = '#,##0.00_-';
const FORMAT_PERCENTAGE = '0%';
const FORMAT_PERCENTAGE_00 = '0.00%';
const FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd';
const FORMAT_DATE_YYYYMMDD = 'yyyy-mm-dd';
const FORMAT_DATE_DDMMYYYY = 'dd/mm/yyyy';
const FORMAT_DATE_DMYSLASH = 'd/m/yy';
const FORMAT_DATE_DMYMINUS = 'd-m-yy';
const FORMAT_DATE_DMMINUS = 'd-m';
const FORMAT_DATE_MYMINUS = 'm-yy';
const FORMAT_DATE_XLSX14 = 'mm-dd-yy';
const FORMAT_DATE_XLSX15 = 'd-mmm-yy';
const FORMAT_DATE_XLSX16 = 'd-mmm';
const FORMAT_DATE_XLSX17 = 'mmm-yy';
const FORMAT_DATE_XLSX22 = 'm/d/yy h:mm';
const FORMAT_DATE_DATETIME = 'd/m/yy h:mm';
const FORMAT_DATE_TIME1 = 'h:mm AM/PM';
const FORMAT_DATE_TIME2 = 'h:mm:ss AM/PM';
const FORMAT_DATE_TIME3 = 'h:mm';
const FORMAT_DATE_TIME4 = 'h:mm:ss';
const FORMAT_DATE_TIME5 = 'mm:ss';
const FORMAT_DATE_TIME6 = 'h:mm:ss';
const FORMAT_DATE_TIME7 = 'i:s.S';
const FORMAT_DATE_TIME8 = 'h:mm:ss;@';
const FORMAT_DATE_YYYYMMDDSLASH = 'yyyy/mm/dd;@';
const FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-';
const FORMAT_CURRENCY_USD = '$#,##0_-';
const FORMAT_CURRENCY_EUR_SIMPLE = '#,##0.00_-"€"';
const FORMAT_CURRENCY_EUR = '#,##0_-"€"';
const FORMAT_ACCOUNTING_USD = '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)';
const FORMAT_ACCOUNTING_EUR = '_("€"* #,##0.00_);_("€"* \(#,##0.00\);_("€"* "-"??_);_(@_)';
/**
* Excel built-in number formats.
*
* @var array
*/
protected static $builtInFormats;
/**
* Excel built-in number formats (flipped, for faster lookups).
*
* @var array
*/
protected static $flippedBuiltInFormats;
/**
* Format Code.
*
* @var string
*/
protected $formatCode = self::FORMAT_GENERAL;
/**
* Built-in format Code.
*
* @var string
*/
protected $builtInFormatCode = 0;
/**
* Create a new NumberFormat.
*
* @param bool $isSupervisor Flag indicating if this is a supervisor or not
* Leave this value at default unless you understand exactly what
* its ramifications are
* @param bool $isConditional Flag indicating if this is a conditional style or not
* Leave this value at default unless you understand exactly what
* its ramifications are
*/
public function __construct($isSupervisor = false, $isConditional = false)
{
// Supervisor?
parent::__construct($isSupervisor);
if ($isConditional) {
$this->formatCode = null;
$this->builtInFormatCode = false;
}
}
/**
* Get the shared style component for the currently active cell in currently active sheet.
* Only used for style supervisor.
*
* @return NumberFormat
*/
public function getSharedComponent()
{
return $this->parent->getSharedComponent()->getNumberFormat();
}
/**
* Build style array from subcomponents.
*
* @param array $array
*
* @return array
*/
public function getStyleArray($array)
{
return ['numberFormat' => $array];
}
/**
* Apply styles from array.
*
* <code>
* $spreadsheet->getActiveSheet()->getStyle('B2')->getNumberFormat()->applyFromArray(
* [
* 'formatCode' => NumberFormat::FORMAT_CURRENCY_EUR_SIMPLE
* ]
* );
* </code>
*
* @param array $pStyles Array containing style information
*
* @return $this
*/
public function applyFromArray(array $pStyles)
{
if ($this->isSupervisor) {
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
} else {
if (isset($pStyles['formatCode'])) {
$this->setFormatCode($pStyles['formatCode']);
}
}
return $this;
}
/**
* Get Format Code.
*
* @return string
*/
public function getFormatCode()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getFormatCode();
}
if ($this->builtInFormatCode !== false) {
return self::builtInFormatCode($this->builtInFormatCode);
}
return $this->formatCode;
}
/**
* Set Format Code.
*
* @param string $pValue see self::FORMAT_*
*
* @return $this
*/
public function setFormatCode($pValue)
{
if ($pValue == '') {
$pValue = self::FORMAT_GENERAL;
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['formatCode' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->formatCode = $pValue;
$this->builtInFormatCode = self::builtInFormatCodeIndex($pValue);
}
return $this;
}
/**
* Get Built-In Format Code.
*
* @return int
*/
public function getBuiltInFormatCode()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getBuiltInFormatCode();
}
return $this->builtInFormatCode;
}
/**
* Set Built-In Format Code.
*
* @param int $pValue
*
* @return $this
*/
public function setBuiltInFormatCode($pValue)
{
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['formatCode' => self::builtInFormatCode($pValue)]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->builtInFormatCode = $pValue;
$this->formatCode = self::builtInFormatCode($pValue);
}
return $this;
}
/**
* Fill built-in format codes.
*/
private static function fillBuiltInFormatCodes(): void
{
// [MS-OI29500: Microsoft Office Implementation Information for ISO/IEC-29500 Standard Compliance]
// 18.8.30. numFmt (Number Format)
//
// The ECMA standard defines built-in format IDs
// 14: "mm-dd-yy"
// 22: "m/d/yy h:mm"
// 37: "#,##0 ;(#,##0)"
// 38: "#,##0 ;[Red](#,##0)"
// 39: "#,##0.00;(#,##0.00)"
// 40: "#,##0.00;[Red](#,##0.00)"
// 47: "mmss.0"
// KOR fmt 55: "yyyy-mm-dd"
// Excel defines built-in format IDs
// 14: "m/d/yyyy"
// 22: "m/d/yyyy h:mm"
// 37: "#,##0_);(#,##0)"
// 38: "#,##0_);[Red](#,##0)"
// 39: "#,##0.00_);(#,##0.00)"
// 40: "#,##0.00_);[Red](#,##0.00)"
// 47: "mm:ss.0"
// KOR fmt 55: "yyyy/mm/dd"
// Built-in format codes
if (self::$builtInFormats === null) {
self::$builtInFormats = [];
// General
self::$builtInFormats[0] = self::FORMAT_GENERAL;
self::$builtInFormats[1] = '0';
self::$builtInFormats[2] = '0.00';
self::$builtInFormats[3] = '#,##0';
self::$builtInFormats[4] = '#,##0.00';
self::$builtInFormats[9] = '0%';
self::$builtInFormats[10] = '0.00%';
self::$builtInFormats[11] = '0.00E+00';
self::$builtInFormats[12] = '# ?/?';
self::$builtInFormats[13] = '# ??/??';
self::$builtInFormats[14] = 'm/d/yyyy'; // Despite ECMA 'mm-dd-yy';
self::$builtInFormats[15] = 'd-mmm-yy';
self::$builtInFormats[16] = 'd-mmm';
self::$builtInFormats[17] = 'mmm-yy';
self::$builtInFormats[18] = 'h:mm AM/PM';
self::$builtInFormats[19] = 'h:mm:ss AM/PM';
self::$builtInFormats[20] = 'h:mm';
self::$builtInFormats[21] = 'h:mm:ss';
self::$builtInFormats[22] = 'm/d/yyyy h:mm'; // Despite ECMA 'm/d/yy h:mm';
self::$builtInFormats[37] = '#,##0_);(#,##0)'; // Despite ECMA '#,##0 ;(#,##0)';
self::$builtInFormats[38] = '#,##0_);[Red](#,##0)'; // Despite ECMA '#,##0 ;[Red](#,##0)';
self::$builtInFormats[39] = '#,##0.00_);(#,##0.00)'; // Despite ECMA '#,##0.00;(#,##0.00)';
self::$builtInFormats[40] = '#,##0.00_);[Red](#,##0.00)'; // Despite ECMA '#,##0.00;[Red](#,##0.00)';
self::$builtInFormats[44] = '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)';
self::$builtInFormats[45] = 'mm:ss';
self::$builtInFormats[46] = '[h]:mm:ss';
self::$builtInFormats[47] = 'mm:ss.0'; // Despite ECMA 'mmss.0';
self::$builtInFormats[48] = '##0.0E+0';
self::$builtInFormats[49] = '@';
// CHT
self::$builtInFormats[27] = '[$-404]e/m/d';
self::$builtInFormats[30] = 'm/d/yy';
self::$builtInFormats[36] = '[$-404]e/m/d';
self::$builtInFormats[50] = '[$-404]e/m/d';
self::$builtInFormats[57] = '[$-404]e/m/d';
// THA
self::$builtInFormats[59] = 't0';
self::$builtInFormats[60] = 't0.00';
self::$builtInFormats[61] = 't#,##0';
self::$builtInFormats[62] = 't#,##0.00';
self::$builtInFormats[67] = 't0%';
self::$builtInFormats[68] = 't0.00%';
self::$builtInFormats[69] = 't# ?/?';
self::$builtInFormats[70] = 't# ??/??';
// JPN
self::$builtInFormats[28] = '[$-411]ggge"年"m"月"d"日"';
self::$builtInFormats[29] = '[$-411]ggge"年"m"月"d"日"';
self::$builtInFormats[31] = 'yyyy"年"m"月"d"日"';
self::$builtInFormats[32] = 'h"時"mm"分"';
self::$builtInFormats[33] = 'h"時"mm"分"ss"秒"';
self::$builtInFormats[34] = 'yyyy"年"m"月"';
self::$builtInFormats[35] = 'm"月"d"日"';
self::$builtInFormats[51] = '[$-411]ggge"年"m"月"d"日"';
self::$builtInFormats[52] = 'yyyy"年"m"月"';
self::$builtInFormats[53] = 'm"月"d"日"';
self::$builtInFormats[54] = '[$-411]ggge"年"m"月"d"日"';
self::$builtInFormats[55] = 'yyyy"年"m"月"';
self::$builtInFormats[56] = 'm"月"d"日"';
self::$builtInFormats[58] = '[$-411]ggge"年"m"月"d"日"';
// Flip array (for faster lookups)
self::$flippedBuiltInFormats = array_flip(self::$builtInFormats);
}
}
/**
* Get built-in format code.
*
* @param int $pIndex
*
* @return string
*/
public static function builtInFormatCode($pIndex)
{
// Clean parameter
$pIndex = (int) $pIndex;
// Ensure built-in format codes are available
self::fillBuiltInFormatCodes();
// Lookup format code
if (isset(self::$builtInFormats[$pIndex])) {
return self::$builtInFormats[$pIndex];
}
return '';
}
/**
* Get built-in format code index.
*
* @param string $formatCode
*
* @return bool|int
*/
public static function builtInFormatCodeIndex($formatCode)
{
// Ensure built-in format codes are available
self::fillBuiltInFormatCodes();
// Lookup format code
if (array_key_exists($formatCode, self::$flippedBuiltInFormats)) {
return self::$flippedBuiltInFormats[$formatCode];
}
return false;
}
/**
* Get hash code.
*
* @return string Hash code
*/
public function getHashCode()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getHashCode();
}
return md5(
$this->formatCode .
$this->builtInFormatCode .
__CLASS__
);
}
/**
* Search/replace values to convert Excel date/time format masks to PHP format masks.
*
* @var array
*/
private static $dateFormatReplacements = [
// first remove escapes related to non-format characters
'\\' => '',
// 12-hour suffix
'am/pm' => 'A',
// 4-digit year
'e' => 'Y',
'yyyy' => 'Y',
// 2-digit year
'yy' => 'y',
// first letter of month - no php equivalent
'mmmmm' => 'M',
// full month name
'mmmm' => 'F',
// short month name
'mmm' => 'M',
// mm is minutes if time, but can also be month w/leading zero
// so we try to identify times be the inclusion of a : separator in the mask
// It isn't perfect, but the best way I know how
':mm' => ':i',
'mm:' => 'i:',
// month leading zero
'mm' => 'm',
// month no leading zero
'm' => 'n',
// full day of week name
'dddd' => 'l',
// short day of week name
'ddd' => 'D',
// days leading zero
'dd' => 'd',
// days no leading zero
'd' => 'j',
// seconds
'ss' => 's',
// fractional seconds - no php equivalent
'.s' => '',
];
/**
* Search/replace values to convert Excel date/time format masks hours to PHP format masks (24 hr clock).
*
* @var array
*/
private static $dateFormatReplacements24 = [
'hh' => 'H',
'h' => 'G',
];
/**
* Search/replace values to convert Excel date/time format masks hours to PHP format masks (12 hr clock).
*
* @var array
*/
private static $dateFormatReplacements12 = [
'hh' => 'h',
'h' => 'g',
];
private static function setLowercaseCallback($matches)
{
return mb_strtolower($matches[0]);
}
private static function escapeQuotesCallback($matches)
{
return '\\' . implode('\\', str_split($matches[1]));
}
private static function formatAsDate(&$value, &$format): void
{
// strip off first part containing e.g. [$-F800] or [$USD-409]
// general syntax: [$<Currency string>-<language info>]
// language info is in hexadecimal
// strip off chinese part like [DBNum1][$-804]
$format = preg_replace('/^(\[[0-9A-Za-z]*\])*(\[\$[A-Z]*-[0-9A-F]*\])/i', '', $format);
// OpenOffice.org uses upper-case number formats, e.g. 'YYYY', convert to lower-case;
// but we don't want to change any quoted strings
$format = preg_replace_callback('/(?:^|")([^"]*)(?:$|")/', ['self', 'setLowercaseCallback'], $format);
// Only process the non-quoted blocks for date format characters
$blocks = explode('"', $format);
foreach ($blocks as $key => &$block) {
if ($key % 2 == 0) {
$block = strtr($block, self::$dateFormatReplacements);
if (!strpos($block, 'A')) {
// 24-hour time format
// when [h]:mm format, the [h] should replace to the hours of the value * 24
if (false !== strpos($block, '[h]')) {
$hours = (int) ($value * 24);
$block = str_replace('[h]', $hours, $block);
continue;
}
$block = strtr($block, self::$dateFormatReplacements24);
} else {
// 12-hour time format
$block = strtr($block, self::$dateFormatReplacements12);
}
}
}
$format = implode('"', $blocks);
// escape any quoted characters so that DateTime format() will render them correctly
$format = preg_replace_callback('/"(.*)"/U', ['self', 'escapeQuotesCallback'], $format);
$dateObj = Date::excelToDateTimeObject($value);
$value = $dateObj->format($format);
}
private static function formatAsPercentage(&$value, &$format): void
{
if ($format === self::FORMAT_PERCENTAGE) {
$value = round((100 * $value), 0) . '%';
} else {
if (preg_match('/\.[#0]+/', $format, $m)) {
$s = substr($m[0], 0, 1) . (strlen($m[0]) - 1);
$format = str_replace($m[0], $s, $format);
}
if (preg_match('/^[#0]+/', $format, $m)) {
$format = str_replace($m[0], strlen($m[0]), $format);
}
$format = '%' . str_replace('%', 'f%%', $format);
$value = sprintf($format, 100 * $value);
}
}
private static function formatAsFraction(&$value, &$format): void
{
$sign = ($value < 0) ? '-' : '';
$integerPart = floor(abs($value));
$decimalPart = trim(fmod(abs($value), 1), '0.');
$decimalLength = strlen($decimalPart);
$decimalDivisor = 10 ** $decimalLength;
$GCD = MathTrig::GCD($decimalPart, $decimalDivisor);
$adjustedDecimalPart = $decimalPart / $GCD;
$adjustedDecimalDivisor = $decimalDivisor / $GCD;
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 = '';
}
$value = "$sign$integerPart $adjustedDecimalPart/$adjustedDecimalDivisor";
} else {
$adjustedDecimalPart += $integerPart * $adjustedDecimalDivisor;
$value = "$sign$adjustedDecimalPart/$adjustedDecimalDivisor";
}
}
private static function mergeComplexNumberFormatMasks($numbers, $masks)
{
$decimalCount = strlen($numbers[1]);
$postDecimalMasks = [];
do {
$tempMask = array_pop($masks);
$postDecimalMasks[] = $tempMask;
$decimalCount -= strlen($tempMask);
} while ($decimalCount > 0);
return [
implode('.', $masks),
implode('.', array_reverse($postDecimalMasks)),
];
}
private static function processComplexNumberFormatMask($number, $mask)
{
$result = $number;
$maskingBlockCount = preg_match_all('/0+/', $mask, $maskingBlocks, PREG_OFFSET_CAPTURE);
if ($maskingBlockCount > 1) {
$maskingBlocks = array_reverse($maskingBlocks[0]);
foreach ($maskingBlocks as $block) {
$divisor = 1 . $block[0];
$size = strlen($block[0]);
$offset = $block[1];
$blockValue = sprintf(
'%0' . $size . 'd',
fmod($number, $divisor)
);
$number = floor($number / $divisor);
$mask = substr_replace($mask, $blockValue, $offset, $size);
}
if ($number > 0) {
$mask = substr_replace($mask, $number, $offset, 0);
}
$result = $mask;
}
return $result;
}
private static function complexNumberFormatMask($number, $mask, $splitOnPoint = true)
{
$sign = ($number < 0.0);
$number = abs($number);
if ($splitOnPoint && strpos($mask, '.') !== false && strpos($number, '.') !== false) {
$numbers = explode('.', $number);
$masks = explode('.', $mask);
if (count($masks) > 2) {
$masks = self::mergeComplexNumberFormatMasks($numbers, $masks);
}
$result1 = self::complexNumberFormatMask($numbers[0], $masks[0], false);
$result2 = strrev(self::complexNumberFormatMask(strrev($numbers[1]), strrev($masks[1]), false));
return (($sign) ? '-' : '') . $result1 . '.' . $result2;
}
$result = self::processComplexNumberFormatMask($number, $mask);
return (($sign) ? '-' : '') . $result;
}
private static function formatStraightNumericValue($value, $format, array $matches, $useThousands, $number_regex)
{
$left = $matches[1];
$dec = $matches[2];
$right = $matches[3];
// minimun width of formatted number (including dot)
$minWidth = strlen($left) + strlen($dec) + strlen($right);
if ($useThousands) {
$value = number_format(
$value,
strlen($right),
StringHelper::getDecimalSeparator(),
StringHelper::getThousandsSeparator()
);
$value = preg_replace($number_regex, $value, $format);
} else {
if (preg_match('/[0#]E[+-]0/i', $format)) {
// Scientific format
$value = sprintf('%5.2E', $value);
} elseif (preg_match('/0([^\d\.]+)0/', $format) || substr_count($format, '.') > 1) {
if ($value == (int) $value && substr_count($format, '.') === 1) {
$value *= 10 ** strlen(explode('.', $format)[1]);
}
$value = self::complexNumberFormatMask($value, $format);
} else {
$sprintf_pattern = "%0$minWidth." . strlen($right) . 'f';
$value = sprintf($sprintf_pattern, $value);
$value = preg_replace($number_regex, $value, $format);
}
}
return $value;
}
private static function formatAsNumber($value, $format)
{
// 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);
// Find out if we need thousands separator
// This is indicated by a comma enclosed by a digit placeholder:
// #,# or 0,0
$useThousands = preg_match('/(#,#|0,0)/', $format);
if ($useThousands) {
$format = preg_replace('/0,0/', '00', $format);
$format = preg_replace('/#,#/', '##', $format);
}
// Scale thousands, millions,...
// This is indicated by a number of commas after a digit placeholder:
// #, or 0.0,,
$scale = 1; // same as no scale
$matches = [];
if (preg_match('/(#|0)(,+)/', $format, $matches)) {
$scale = 1000 ** strlen($matches[2]);
// strip the commas
$format = preg_replace('/0,+/', '0', $format);
$format = preg_replace('/#,+/', '#', $format);
}
if (preg_match('/#?.*\?\/\?/', $format, $m)) {
if ($value != (int) $value) {
self::formatAsFraction($value, $format);
}
} else {
// Handle the number itself
// scale number
$value = $value / $scale;
// Strip #
$format = preg_replace('/\\#/', '0', $format);
// Remove locale code [$-###]
$format = preg_replace('/\[\$\-.*\]/', '', $format);
$n = '/\\[[^\\]]+\\]/';
$m = preg_replace($n, '', $format);
$number_regex = '/(0+)(\\.?)(0*)/';
if (preg_match($number_regex, $m, $matches)) {
$value = self::formatStraightNumericValue($value, $format, $matches, $useThousands, $number_regex);
}
}
if (preg_match('/\[\$(.*)\]/u', $format, $m)) {
// Currency or Accounting
$currencyCode = $m[1];
[$currencyCode] = explode('-', $currencyCode);
if ($currencyCode == '') {
$currencyCode = StringHelper::getCurrencyCode();
}
$value = preg_replace('/\[\$([^\]]*)\]/u', $currencyCode, $value);
}
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.
*
* @param mixed $value Value to format
* @param string $format Format code, see = self::FORMAT_*
* @param array $callBack Callback function for additional formatting of string
*
* @return string Formatted string
*/
public static function toFormattedString($value, $format, $callBack = null)
{
// For now we do not treat strings although section 4 of a format code affects strings
if (!is_numeric($value)) {
return $value;
}
// For 'General' format code, we just pass the value although this is not entirely the way Excel does it,
// it seems to round numbers to a total of 10 digits.
if (($format === self::FORMAT_GENERAL) || ($format === self::FORMAT_TEXT)) {
return $value;
}
// Convert any other escaped characters to quoted strings, e.g. (\T to "T")
$format = preg_replace('/(\\\(((.)(?!((AM\/PM)|(A\/P))))|([^ ])))(?=(?:[^"]|"[^"]*")*$)/u', '"${2}"', $format);
// 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);
[$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);
// Let's begin inspecting the format and converting the value to a formatted string
// Check for date/time characters (not inside quotes)
if (preg_match('/(\[\$[A-Z]*-[0-9A-F]*\])*[hmsdy](?=(?:[^"]|"[^"]*")*$)/miu', $format, $matches)) {
// datetime format
self::formatAsDate($value, $format);
} else {
if (substr($format, 0, 1) === '"' && substr($format, -1, 1) === '"') {
$value = substr($format, 1, -1);
} elseif (preg_match('/%$/', $format)) {
// % number format
self::formatAsPercentage($value, $format);
} else {
$value = self::formatAsNumber($value, $format);
}
}
// Additional formatting provided by callback function
if ($callBack !== null) {
[$writerInstance, $function] = $callBack;
$value = $writerInstance->$function($value, $colors);
}
return $value;
}
protected function exportArray1(): array
{
$exportedArray = [];
$this->exportArray2($exportedArray, 'formatCode', $this->getFormatCode());
return $exportedArray;
}
}

View File

@@ -0,0 +1,195 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Style;
class Protection extends Supervisor
{
/** Protection styles */
const PROTECTION_INHERIT = 'inherit';
const PROTECTION_PROTECTED = 'protected';
const PROTECTION_UNPROTECTED = 'unprotected';
/**
* Locked.
*
* @var string
*/
protected $locked;
/**
* Hidden.
*
* @var string
*/
protected $hidden;
/**
* Create a new Protection.
*
* @param bool $isSupervisor Flag indicating if this is a supervisor or not
* Leave this value at default unless you understand exactly what
* its ramifications are
* @param bool $isConditional Flag indicating if this is a conditional style or not
* Leave this value at default unless you understand exactly what
* its ramifications are
*/
public function __construct($isSupervisor = false, $isConditional = false)
{
// Supervisor?
parent::__construct($isSupervisor);
// Initialise values
if (!$isConditional) {
$this->locked = self::PROTECTION_INHERIT;
$this->hidden = self::PROTECTION_INHERIT;
}
}
/**
* Get the shared style component for the currently active cell in currently active sheet.
* Only used for style supervisor.
*
* @return Protection
*/
public function getSharedComponent()
{
return $this->parent->getSharedComponent()->getProtection();
}
/**
* Build style array from subcomponents.
*
* @param array $array
*
* @return array
*/
public function getStyleArray($array)
{
return ['protection' => $array];
}
/**
* Apply styles from array.
*
* <code>
* $spreadsheet->getActiveSheet()->getStyle('B2')->getLocked()->applyFromArray(
* [
* 'locked' => TRUE,
* 'hidden' => FALSE
* ]
* );
* </code>
*
* @param array $pStyles Array containing style information
*
* @return $this
*/
public function applyFromArray(array $pStyles)
{
if ($this->isSupervisor) {
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
} else {
if (isset($pStyles['locked'])) {
$this->setLocked($pStyles['locked']);
}
if (isset($pStyles['hidden'])) {
$this->setHidden($pStyles['hidden']);
}
}
return $this;
}
/**
* Get locked.
*
* @return string
*/
public function getLocked()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getLocked();
}
return $this->locked;
}
/**
* Set locked.
*
* @param string $pValue see self::PROTECTION_*
*
* @return $this
*/
public function setLocked($pValue)
{
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['locked' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->locked = $pValue;
}
return $this;
}
/**
* Get hidden.
*
* @return string
*/
public function getHidden()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getHidden();
}
return $this->hidden;
}
/**
* Set hidden.
*
* @param string $pValue see self::PROTECTION_*
*
* @return $this
*/
public function setHidden($pValue)
{
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['hidden' => $pValue]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->hidden = $pValue;
}
return $this;
}
/**
* Get hash code.
*
* @return string Hash code
*/
public function getHashCode()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getHashCode();
}
return md5(
$this->locked .
$this->hidden .
__CLASS__
);
}
protected function exportArray1(): array
{
$exportedArray = [];
$this->exportArray2($exportedArray, 'locked', $this->getLocked());
$this->exportArray2($exportedArray, 'hidden', $this->getHidden());
return $exportedArray;
}
}

View File

@@ -0,0 +1,655 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Style;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
class Style extends Supervisor
{
/**
* Font.
*
* @var Font
*/
protected $font;
/**
* Fill.
*
* @var Fill
*/
protected $fill;
/**
* Borders.
*
* @var Borders
*/
protected $borders;
/**
* Alignment.
*
* @var Alignment
*/
protected $alignment;
/**
* Number Format.
*
* @var NumberFormat
*/
protected $numberFormat;
/**
* Protection.
*
* @var Protection
*/
protected $protection;
/**
* Index of style in collection. Only used for real style.
*
* @var int
*/
protected $index;
/**
* Use Quote Prefix when displaying in cell editor. Only used for real style.
*
* @var bool
*/
protected $quotePrefix = false;
/**
* Create a new Style.
*
* @param bool $isSupervisor Flag indicating if this is a supervisor or not
* Leave this value at default unless you understand exactly what
* its ramifications are
* @param bool $isConditional Flag indicating if this is a conditional style or not
* Leave this value at default unless you understand exactly what
* its ramifications are
*/
public function __construct($isSupervisor = false, $isConditional = false)
{
parent::__construct($isSupervisor);
// Initialise values
$this->font = new Font($isSupervisor, $isConditional);
$this->fill = new Fill($isSupervisor, $isConditional);
$this->borders = new Borders($isSupervisor, $isConditional);
$this->alignment = new Alignment($isSupervisor, $isConditional);
$this->numberFormat = new NumberFormat($isSupervisor, $isConditional);
$this->protection = new Protection($isSupervisor, $isConditional);
// bind parent if we are a supervisor
if ($isSupervisor) {
$this->font->bindParent($this);
$this->fill->bindParent($this);
$this->borders->bindParent($this);
$this->alignment->bindParent($this);
$this->numberFormat->bindParent($this);
$this->protection->bindParent($this);
}
}
/**
* Get the shared style component for the currently active cell in currently active sheet.
* Only used for style supervisor.
*
* @return Style
*/
public function getSharedComponent()
{
$activeSheet = $this->getActiveSheet();
$selectedCell = $this->getActiveCell(); // e.g. 'A1'
if ($activeSheet->cellExists($selectedCell)) {
$xfIndex = $activeSheet->getCell($selectedCell)->getXfIndex();
} else {
$xfIndex = 0;
}
return $this->parent->getCellXfByIndex($xfIndex);
}
/**
* Get parent. Only used for style supervisor.
*
* @return Spreadsheet
*/
public function getParent()
{
return $this->parent;
}
/**
* Build style array from subcomponents.
*
* @param array $array
*
* @return array
*/
public function getStyleArray($array)
{
return ['quotePrefix' => $array];
}
/**
* Apply styles from array.
*
* <code>
* $spreadsheet->getActiveSheet()->getStyle('B2')->applyFromArray(
* [
* 'font' => [
* 'name' => 'Arial',
* 'bold' => true,
* 'italic' => false,
* 'underline' => Font::UNDERLINE_DOUBLE,
* 'strikethrough' => false,
* 'color' => [
* 'rgb' => '808080'
* ]
* ],
* 'borders' => [
* 'bottom' => [
* 'borderStyle' => Border::BORDER_DASHDOT,
* 'color' => [
* 'rgb' => '808080'
* ]
* ],
* 'top' => [
* 'borderStyle' => Border::BORDER_DASHDOT,
* 'color' => [
* 'rgb' => '808080'
* ]
* ]
* ],
* 'alignment' => [
* 'horizontal' => Alignment::HORIZONTAL_CENTER,
* 'vertical' => Alignment::VERTICAL_CENTER,
* 'wrapText' => true,
* ],
* 'quotePrefix' => true
* ]
* );
* </code>
*
* @param array $pStyles Array containing style information
* @param bool $pAdvanced advanced mode for setting borders
*
* @return $this
*/
public function applyFromArray(array $pStyles, $pAdvanced = true)
{
if ($this->isSupervisor) {
$pRange = $this->getSelectedCells();
// Uppercase coordinate
$pRange = strtoupper($pRange);
// Is it a cell range or a single cell?
if (strpos($pRange, ':') === false) {
$rangeA = $pRange;
$rangeB = $pRange;
} else {
[$rangeA, $rangeB] = explode(':', $pRange);
}
// Calculate range outer borders
$rangeStart = Coordinate::coordinateFromString($rangeA);
$rangeEnd = Coordinate::coordinateFromString($rangeB);
// Translate column into index
$rangeStart0 = $rangeStart[0];
$rangeEnd0 = $rangeEnd[0];
$rangeStart[0] = Coordinate::columnIndexFromString($rangeStart[0]);
$rangeEnd[0] = Coordinate::columnIndexFromString($rangeEnd[0]);
// Make sure we can loop upwards on rows and columns
if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
$tmp = $rangeStart;
$rangeStart = $rangeEnd;
$rangeEnd = $tmp;
}
// ADVANCED MODE:
if ($pAdvanced && isset($pStyles['borders'])) {
// 'allBorders' is a shorthand property for 'outline' and 'inside' and
// it applies to components that have not been set explicitly
if (isset($pStyles['borders']['allBorders'])) {
foreach (['outline', 'inside'] as $component) {
if (!isset($pStyles['borders'][$component])) {
$pStyles['borders'][$component] = $pStyles['borders']['allBorders'];
}
}
unset($pStyles['borders']['allBorders']); // not needed any more
}
// 'outline' is a shorthand property for 'top', 'right', 'bottom', 'left'
// it applies to components that have not been set explicitly
if (isset($pStyles['borders']['outline'])) {
foreach (['top', 'right', 'bottom', 'left'] as $component) {
if (!isset($pStyles['borders'][$component])) {
$pStyles['borders'][$component] = $pStyles['borders']['outline'];
}
}
unset($pStyles['borders']['outline']); // not needed any more
}
// 'inside' is a shorthand property for 'vertical' and 'horizontal'
// it applies to components that have not been set explicitly
if (isset($pStyles['borders']['inside'])) {
foreach (['vertical', 'horizontal'] as $component) {
if (!isset($pStyles['borders'][$component])) {
$pStyles['borders'][$component] = $pStyles['borders']['inside'];
}
}
unset($pStyles['borders']['inside']); // not needed any more
}
// width and height characteristics of selection, 1, 2, or 3 (for 3 or more)
$xMax = min($rangeEnd[0] - $rangeStart[0] + 1, 3);
$yMax = min($rangeEnd[1] - $rangeStart[1] + 1, 3);
// loop through up to 3 x 3 = 9 regions
for ($x = 1; $x <= $xMax; ++$x) {
// start column index for region
$colStart = ($x == 3) ?
Coordinate::stringFromColumnIndex($rangeEnd[0])
: Coordinate::stringFromColumnIndex($rangeStart[0] + $x - 1);
// end column index for region
$colEnd = ($x == 1) ?
Coordinate::stringFromColumnIndex($rangeStart[0])
: Coordinate::stringFromColumnIndex($rangeEnd[0] - $xMax + $x);
for ($y = 1; $y <= $yMax; ++$y) {
// which edges are touching the region
$edges = [];
if ($x == 1) {
// are we at left edge
$edges[] = 'left';
}
if ($x == $xMax) {
// are we at right edge
$edges[] = 'right';
}
if ($y == 1) {
// are we at top edge?
$edges[] = 'top';
}
if ($y == $yMax) {
// are we at bottom edge?
$edges[] = 'bottom';
}
// start row index for region
$rowStart = ($y == 3) ?
$rangeEnd[1] : $rangeStart[1] + $y - 1;
// end row index for region
$rowEnd = ($y == 1) ?
$rangeStart[1] : $rangeEnd[1] - $yMax + $y;
// build range for region
$range = $colStart . $rowStart . ':' . $colEnd . $rowEnd;
// retrieve relevant style array for region
$regionStyles = $pStyles;
unset($regionStyles['borders']['inside']);
// what are the inner edges of the region when looking at the selection
$innerEdges = array_diff(['top', 'right', 'bottom', 'left'], $edges);
// inner edges that are not touching the region should take the 'inside' border properties if they have been set
foreach ($innerEdges as $innerEdge) {
switch ($innerEdge) {
case 'top':
case 'bottom':
// should pick up 'horizontal' border property if set
if (isset($pStyles['borders']['horizontal'])) {
$regionStyles['borders'][$innerEdge] = $pStyles['borders']['horizontal'];
} else {
unset($regionStyles['borders'][$innerEdge]);
}
break;
case 'left':
case 'right':
// should pick up 'vertical' border property if set
if (isset($pStyles['borders']['vertical'])) {
$regionStyles['borders'][$innerEdge] = $pStyles['borders']['vertical'];
} else {
unset($regionStyles['borders'][$innerEdge]);
}
break;
}
}
// apply region style to region by calling applyFromArray() in simple mode
$this->getActiveSheet()->getStyle($range)->applyFromArray($regionStyles, false);
}
}
// restore initial cell selection range
$this->getActiveSheet()->getStyle($pRange);
return $this;
}
// SIMPLE MODE:
// Selection type, inspect
if (preg_match('/^[A-Z]+1:[A-Z]+1048576$/', $pRange)) {
$selectionType = 'COLUMN';
} elseif (preg_match('/^A\d+:XFD\d+$/', $pRange)) {
$selectionType = 'ROW';
} else {
$selectionType = 'CELL';
}
// First loop through columns, rows, or cells to find out which styles are affected by this operation
switch ($selectionType) {
case 'COLUMN':
$oldXfIndexes = [];
for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
$oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true;
}
foreach ($this->getActiveSheet()->getColumnIterator($rangeStart0, $rangeEnd0) as $columnIterator) {
$cellIterator = $columnIterator->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(true);
foreach ($cellIterator as $columnCell) {
$columnCell->getStyle()->applyFromArray($pStyles);
}
}
break;
case 'ROW':
$oldXfIndexes = [];
for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
if ($this->getActiveSheet()->getRowDimension($row)->getXfIndex() == null) {
$oldXfIndexes[0] = true; // row without explicit style should be formatted based on default style
} else {
$oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true;
}
}
foreach ($this->getActiveSheet()->getRowIterator((int) $rangeStart[1], (int) $rangeEnd[1]) as $rowIterator) {
$cellIterator = $rowIterator->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(true);
foreach ($cellIterator as $rowCell) {
$rowCell->getStyle()->applyFromArray($pStyles);
}
}
break;
case 'CELL':
$oldXfIndexes = [];
for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
$oldXfIndexes[$this->getActiveSheet()->getCellByColumnAndRow($col, $row)->getXfIndex()] = true;
}
}
break;
}
// clone each of the affected styles, apply the style array, and add the new styles to the workbook
$workbook = $this->getActiveSheet()->getParent();
foreach ($oldXfIndexes as $oldXfIndex => $dummy) {
$style = $workbook->getCellXfByIndex($oldXfIndex);
$newStyle = clone $style;
$newStyle->applyFromArray($pStyles);
if ($existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode())) {
// there is already such cell Xf in our collection
$newXfIndexes[$oldXfIndex] = $existingStyle->getIndex();
} else {
// we don't have such a cell Xf, need to add
$workbook->addCellXf($newStyle);
$newXfIndexes[$oldXfIndex] = $newStyle->getIndex();
}
}
// Loop through columns, rows, or cells again and update the XF index
switch ($selectionType) {
case 'COLUMN':
for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
$columnDimension = $this->getActiveSheet()->getColumnDimensionByColumn($col);
$oldXfIndex = $columnDimension->getXfIndex();
$columnDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
}
break;
case 'ROW':
for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
$rowDimension = $this->getActiveSheet()->getRowDimension($row);
$oldXfIndex = $rowDimension->getXfIndex() === null ?
0 : $rowDimension->getXfIndex(); // row without explicit style should be formatted based on default style
$rowDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
}
break;
case 'CELL':
for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
$cell = $this->getActiveSheet()->getCellByColumnAndRow($col, $row);
$oldXfIndex = $cell->getXfIndex();
$cell->setXfIndex($newXfIndexes[$oldXfIndex]);
}
}
break;
}
} else {
// not a supervisor, just apply the style array directly on style object
if (isset($pStyles['fill'])) {
$this->getFill()->applyFromArray($pStyles['fill']);
}
if (isset($pStyles['font'])) {
$this->getFont()->applyFromArray($pStyles['font']);
}
if (isset($pStyles['borders'])) {
$this->getBorders()->applyFromArray($pStyles['borders']);
}
if (isset($pStyles['alignment'])) {
$this->getAlignment()->applyFromArray($pStyles['alignment']);
}
if (isset($pStyles['numberFormat'])) {
$this->getNumberFormat()->applyFromArray($pStyles['numberFormat']);
}
if (isset($pStyles['protection'])) {
$this->getProtection()->applyFromArray($pStyles['protection']);
}
if (isset($pStyles['quotePrefix'])) {
$this->quotePrefix = $pStyles['quotePrefix'];
}
}
return $this;
}
/**
* Get Fill.
*
* @return Fill
*/
public function getFill()
{
return $this->fill;
}
/**
* Get Font.
*
* @return Font
*/
public function getFont()
{
return $this->font;
}
/**
* Set font.
*
* @return $this
*/
public function setFont(Font $font)
{
$this->font = $font;
return $this;
}
/**
* Get Borders.
*
* @return Borders
*/
public function getBorders()
{
return $this->borders;
}
/**
* Get Alignment.
*
* @return Alignment
*/
public function getAlignment()
{
return $this->alignment;
}
/**
* Get Number Format.
*
* @return NumberFormat
*/
public function getNumberFormat()
{
return $this->numberFormat;
}
/**
* Get Conditional Styles. Only used on supervisor.
*
* @return Conditional[]
*/
public function getConditionalStyles()
{
return $this->getActiveSheet()->getConditionalStyles($this->getActiveCell());
}
/**
* Set Conditional Styles. Only used on supervisor.
*
* @param Conditional[] $pValue Array of conditional styles
*
* @return $this
*/
public function setConditionalStyles(array $pValue)
{
$this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $pValue);
return $this;
}
/**
* Get Protection.
*
* @return Protection
*/
public function getProtection()
{
return $this->protection;
}
/**
* Get quote prefix.
*
* @return bool
*/
public function getQuotePrefix()
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getQuotePrefix();
}
return $this->quotePrefix;
}
/**
* Set quote prefix.
*
* @param bool $pValue
*
* @return $this
*/
public function setQuotePrefix($pValue)
{
if ($pValue == '') {
$pValue = false;
}
if ($this->isSupervisor) {
$styleArray = ['quotePrefix' => $pValue];
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->quotePrefix = (bool) $pValue;
}
return $this;
}
/**
* Get hash code.
*
* @return string Hash code
*/
public function getHashCode()
{
return md5(
$this->fill->getHashCode() .
$this->font->getHashCode() .
$this->borders->getHashCode() .
$this->alignment->getHashCode() .
$this->numberFormat->getHashCode() .
$this->protection->getHashCode() .
($this->quotePrefix ? 't' : 'f') .
__CLASS__
);
}
/**
* Get own index in style collection.
*
* @return int
*/
public function getIndex()
{
return $this->index;
}
/**
* Set own index in style collection.
*
* @param int $pValue
*/
public function setIndex($pValue): void
{
$this->index = $pValue;
}
protected function exportArray1(): array
{
$exportedArray = [];
$this->exportArray2($exportedArray, 'alignment', $this->getAlignment());
$this->exportArray2($exportedArray, 'borders', $this->getBorders());
$this->exportArray2($exportedArray, 'fill', $this->getFill());
$this->exportArray2($exportedArray, 'font', $this->getFont());
$this->exportArray2($exportedArray, 'numberFormat', $this->getNumberFormat());
$this->exportArray2($exportedArray, 'protection', $this->getProtection());
$this->exportArray2($exportedArray, 'quotePrefx', $this->getQuotePrefix());
return $exportedArray;
}
}

View File

@@ -0,0 +1,158 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Style;
use PhpOffice\PhpSpreadsheet\IComparable;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
abstract class Supervisor implements IComparable
{
/**
* Supervisor?
*
* @var bool
*/
protected $isSupervisor;
/**
* Parent. Only used for supervisor.
*
* @var Spreadsheet|Style
*/
protected $parent;
/**
* Parent property name.
*
* @var null|string
*/
protected $parentPropertyName;
/**
* Create a new Supervisor.
*
* @param bool $isSupervisor Flag indicating if this is a supervisor or not
* Leave this value at default unless you understand exactly what
* its ramifications are
*/
public function __construct($isSupervisor = false)
{
// Supervisor?
$this->isSupervisor = $isSupervisor;
}
/**
* Bind parent. Only used for supervisor.
*
* @param Spreadsheet|Style $parent
* @param null|string $parentPropertyName
*
* @return $this
*/
public function bindParent($parent, $parentPropertyName = null)
{
$this->parent = $parent;
$this->parentPropertyName = $parentPropertyName;
return $this;
}
/**
* Is this a supervisor or a cell style component?
*
* @return bool
*/
public function getIsSupervisor()
{
return $this->isSupervisor;
}
/**
* Get the currently active sheet. Only used for supervisor.
*
* @return Worksheet
*/
public function getActiveSheet()
{
return $this->parent->getActiveSheet();
}
/**
* Get the currently active cell coordinate in currently active sheet.
* Only used for supervisor.
*
* @return string E.g. 'A1'
*/
public function getSelectedCells()
{
return $this->getActiveSheet()->getSelectedCells();
}
/**
* Get the currently active cell coordinate in currently active sheet.
* Only used for supervisor.
*
* @return string E.g. 'A1'
*/
public function getActiveCell()
{
return $this->getActiveSheet()->getActiveCell();
}
/**
* Implement PHP __clone to create a deep clone, not just a shallow copy.
*/
public function __clone()
{
$vars = get_object_vars($this);
foreach ($vars as $key => $value) {
if ((is_object($value)) && ($key != 'parent')) {
$this->$key = clone $value;
} else {
$this->$key = $value;
}
}
}
/**
* Export style as array.
*
* Available to anything which extends this class:
* Alignment, Border, Borders, Color, Fill, Font,
* NumberFormat, Protection, and Style.
*/
final public function exportArray(): array
{
return $this->exportArray1();
}
/**
* Abstract method to be implemented in anything which
* extends this class.
*
* This method invokes exportArray2 with the names and values
* of all properties to be included in output array,
* returning that array to exportArray, then to caller.
*/
abstract protected function exportArray1(): array;
/**
* Populate array from exportArray1.
* This method is available to anything which extends this class.
* The parameter index is the key to be added to the array.
* The parameter objOrValue is either a primitive type,
* which is the value added to the array,
* or a Style object to be recursively added via exportArray.
*
* @param mixed $objOrValue
*/
final protected function exportArray2(array &$exportedArray, string $index, $objOrValue): void
{
if ($objOrValue instanceof self) {
$exportedArray[$index] = $objOrValue->exportArray();
} else {
$exportedArray[$index] = $objOrValue;
}
}
}