mirror of
https://github.com/shuchkin/simplexlsxgen.git
synced 2023-08-10 21:12:59 +03:00
Methods separation
This commit is contained in:
parent
f0b003ff4f
commit
7ba94991dd
@ -1337,169 +1337,4 @@ class SimpleXLSXGen
|
||||
return $c . strval($y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert A1 range reference format to R1C1 range reference format
|
||||
*
|
||||
* @param string $range Range reference in A1 format
|
||||
*
|
||||
* @return array Range reference in R1C1 format as a 4-element integer array: [top-left row, top-left col, bottom-right row, bottom-right col]
|
||||
*/
|
||||
public static function range2coord($range)
|
||||
{
|
||||
$temp = explode(':', $range);
|
||||
if (empty($temp[1])) return self::cell2coord($temp[0]);
|
||||
return array_merge(self::cell2coord($temp[0]), self::cell2coord($temp[1]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert R1C1 range reference format (row/col 1-based) to A1 range reference format
|
||||
*
|
||||
* Possible parameters:
|
||||
* top-left row number, top-left column number, bottom-right row number, bottom-right column number
|
||||
* [top-left row number, top-left column number], [bottom-right row number, bottom-right column number]
|
||||
* [top-left row number, top-left column number, bottom-right row number, bottom-right column number]
|
||||
*
|
||||
* @return string Range reference in A1 format
|
||||
*/
|
||||
public static function coord2range($p1, ...$p)
|
||||
{
|
||||
switch (count($p)) {
|
||||
case 0: $yi = $p1[0]; $xi = $p1[1]; $yf = $p1[2]; $xf = $p1[3]; break;
|
||||
case 1: $yi = $p1[0]; $xi = $p1[1]; $yf = $p[0][0]; $xf = $p[0][1]; break;
|
||||
default: $yi = $p1; $xi = $p[0]; $yf = $p[1]; $xf = $p[2]; break;
|
||||
}
|
||||
return self::coord2cell($yi, $xi) . ':' . self::coord2cell($yf, $xf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change or add cell style parameters without losing the original content or previously established styles
|
||||
*
|
||||
* @param string $data Data matrix (2-dim 0-based array) where style will be modified. Passed by reference.
|
||||
* @param string|array $cell Cell to modify in A1 format (string) or R1C1 format (2 integer elements array)
|
||||
* @param array $style Associative array with style attributes name/value pairs to change/add. You can use wildcard for
|
||||
* specific borders to retain original value.
|
||||
* @param integer $edge Optional. If border attribute specified in $style, this parameter indicates on which edge is applied.
|
||||
* The unspecified edges retain their original value (or 'none' if not specified).
|
||||
* Default: all edges. You can combine (adding or or-ing) the constants A_TOP, A_RIGHT, A_BOTTOM, A_LEFT
|
||||
* @return void
|
||||
*/
|
||||
public static function setCellStyle(&$data, $cell, $style, $edge = self::A_DEFAULT)
|
||||
{
|
||||
if (is_string($cell)) $cell = self::cell2coord($cell);//A1 to R1C1 if needed
|
||||
if (!array_key_exists($cell[0] - 1, $data) || !array_key_exists($cell[1] - 1, $data[$cell[0] - 1])) return;//quit if cell doesn't exist
|
||||
if ($edge === self::A_DEFAULT) $edge = self::A_TOP + self::A_RIGHT + self::A_BOTTOM + self::A_LEFT;
|
||||
//border processing
|
||||
if (array_key_exists('border', $style)) {
|
||||
//get original border as 4 element array
|
||||
$oldBorder = self::getTagAttributes($data[$cell[0] - 1][$cell[1] - 1], 'style', 'border');
|
||||
if (empty($oldBorder)) {
|
||||
$oldBorder = ['none', 'none', 'none', 'none']; //if border not present in original cell, create a no-border attribute
|
||||
} else {
|
||||
$oldBorder = explode(' ', $oldBorder);
|
||||
if (count($oldBorder) == 1) $oldBorder = [$oldBorder[0], $oldBorder[0], $oldBorder[0], $oldBorder[0]];
|
||||
}
|
||||
//get desired border as 4 element array
|
||||
$border = explode(' ', $style['border']);
|
||||
if (count($border) == 1) $border = [$border[0], $border[0], $border[0], $border[0]];
|
||||
//new border copied from old one
|
||||
$newBorder = $oldBorder;
|
||||
//set border as indicated by $edge, except when $border has wildcard
|
||||
if ($edge & self::A_TOP && $border[0] != '*') $newBorder[0] = $border[0];
|
||||
if ($edge & self::A_RIGHT && $border[1] != '*') $newBorder[1] = $border[1];
|
||||
if ($edge & self::A_BOTTOM && $border[2] != '*') $newBorder[2] = $border[2];
|
||||
if ($edge & self::A_LEFT && $border[3] != '*') $newBorder[3] = $border[3];
|
||||
//implode to the new border
|
||||
$style['border'] = implode(' ', $newBorder);
|
||||
}
|
||||
$data[$cell[0] - 1][$cell[1] - 1] = self::setTagAttributes($data[$cell[0] - 1][$cell[1] - 1], 'style', $style);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply style to a sheet range. Border style are only applied to edges of range.
|
||||
*
|
||||
* @param array $data Data matrix (2-dim 0-based array) where style will be modified. Passed by reference.
|
||||
* @param string|array $range Range to modify in A1 format (string) or R1C1 format (4-element integer array)
|
||||
* @param array $style Associative array with style attributes name/value pairs to change
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setRangeStyle(&$data, $range, $style)
|
||||
{
|
||||
if (is_string($range)) $range = self::range2coord($range);
|
||||
// 1 cell range
|
||||
if (!isset($range[2])) return self::setCellStyle($data, $range, $style); //1 cell case
|
||||
// Edge of range
|
||||
self::setCellStyle($data, [$range[0], $range[1]], $style, self::A_TOP + self::A_LEFT); //top-left corner
|
||||
for ($i = $range[1] + 1; $i < $range[3]; $i++) self::setCellStyle($data, [$range[0], $i], $style, self::A_TOP); //loop top edge (except first and last column)
|
||||
self::setCellStyle($data, [$range[0], $range[3]], $style, self::A_TOP + self::A_RIGHT); //top-right corner
|
||||
for ($i = $range[0] + 1; $i < $range[2]; $i++) self::setCellStyle($data, [$i, $range[3]], $style, self::A_RIGHT); //loop right edge (except top and bottom row)
|
||||
self::setCellStyle($data, [$range[2], $range[3]], $style, self::A_BOTTOM + self::A_RIGHT); //bottom-right corner
|
||||
for ($i = $range[3] - 1; $i > $range[1]; $i--) self::setCellStyle($data, [$range[2], $i], $style, self::A_BOTTOM); //loop bottom edge (except right and left column)
|
||||
self::setCellStyle($data, [$range[2], $range[1]], $style, self::A_BOTTOM + self::A_LEFT); //bottom-left corner
|
||||
for ($i = $range[2] - 1; $i > $range[0]; $i--) self::setCellStyle($data, [$i, $range[1]], $style, self::A_LEFT); //loop left edge (except bottom and top row)
|
||||
// Internal cells
|
||||
if (array_key_exists('border', $style)) unset($style['border']);
|
||||
for ($i = $range[0] + 1; $i < $range[2]; $i++) {
|
||||
for ($j = $range[1] + 1; $j < $range[3]; $j++) {
|
||||
self::setCellStyle($data, [$i, $j], $style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tag attributes from a HTML-style tag as an associative array with attributes name/value pairs.
|
||||
* If $attribute specified, get value of the specified attribute.
|
||||
*
|
||||
* @param string $str Text to extract tag attributes
|
||||
* @param string $tag The tag to look for
|
||||
* @param string $attribute Optional. Get the value of specified attribute. Default: get all attributes name/value as an array.
|
||||
*
|
||||
* @return array|string Attributes name/value as an array ($attribute == '') or attribute value as a string ($attribute != '').
|
||||
*/
|
||||
public static function getTagAttributes($str, $tag, $attribute = '')
|
||||
{
|
||||
$attributes = ($attribute === '') ? [] : '';
|
||||
if (preg_match("/<{$tag}\s+([^>]+)(?:>)/i", $str, $m)) {
|
||||
$tagcontent = $m[1];
|
||||
if (preg_match_all('/([a-z0-9\-_]+)=(["\'])(.*?)\2/si', $tagcontent, $m, PREG_SET_ORDER)) {
|
||||
foreach ($m as $match) {
|
||||
if ($attribute !== '') {
|
||||
if ($attribute === $match[1]) return $match[3];
|
||||
} else {
|
||||
$attributes[$match[1]] = $match[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change HTML-style tag attributes. If tag doesn't exist, is created surrounding the previous content.
|
||||
*
|
||||
* @param string $str Text to modify
|
||||
* @param string $tag Tag in text to modify
|
||||
* @param array $attributes Associative array with attribute name/value pairs
|
||||
*
|
||||
* @return string Modified text
|
||||
*/
|
||||
public static function setTagAttributes($str, $tag, $attributes)
|
||||
{
|
||||
if (empty($attributes)) return $str;
|
||||
$opening = "<{$tag}";
|
||||
if (preg_match("/(<{$tag}\s*[^>]*)(>.*<\/{$tag}>)/i", $str, $m)) {
|
||||
$stropen = $m[1];
|
||||
} else {
|
||||
$str = "<{$tag}>{$str}</{$tag}>";
|
||||
$stropen = $opening;
|
||||
}
|
||||
//merge original attributes and specified attributes (replacing if necessary)
|
||||
$attributes = array_merge(self::getTagAttributes($str, $tag), $attributes);
|
||||
//add attributes to tag
|
||||
foreach ($attributes as $attr => $value) $opening .= " {$attr}=\"{$value}\"";
|
||||
//replace original tag (with attributes) with the new one
|
||||
$str = substr_replace($str, $opening, strpos($str, $stropen), strlen($stropen));
|
||||
return $str;
|
||||
}
|
||||
|
||||
}
|
||||
|
246
src/SimpleXLSXGenEx.php
Normal file
246
src/SimpleXLSXGenEx.php
Normal file
@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
/** @noinspection ReturnTypeCanBeDeclaredInspection */
|
||||
/** @noinspection PhpMissingReturnTypeInspection */
|
||||
/** @noinspection NullCoalescingOperatorCanBeUsedInspection */
|
||||
/** @noinspection PhpIssetCanBeReplacedWithCoalesceInspection */
|
||||
|
||||
namespace Shuchkin;
|
||||
|
||||
/**
|
||||
* Class SimpleXLSXGenEx
|
||||
*
|
||||
* Helper methods to manage sheet data. It needs SimpleXLSXGen class.
|
||||
*
|
||||
* @author Sergey Shuchkin <sergey.shuchkin@gmail.com>
|
||||
*/
|
||||
class SimpleXLSXGenEx
|
||||
{
|
||||
const EDGE_LEFT = 1;
|
||||
const EDGE_RIGHT = 2;
|
||||
const EDGE_TOP = 4;
|
||||
const EDGE_BOTTOM = 8;
|
||||
const EDGE_ALL = self::EDGE_LEFT + self::EDGE_RIGHT + self::EDGE_TOP + self::EDGE_BOTTOM;
|
||||
|
||||
/**
|
||||
* Convert A1 cell reference format to R1C1 cell reference format (row/col number starting from 1)
|
||||
*
|
||||
* @param string $cell Cell reference in A1 format
|
||||
*
|
||||
* @return array Cell reference in R1C1 format as a 2-element integer array: [row (ie. y coord), col (ie. x coord)]
|
||||
*/
|
||||
public static function cell2coord($cell)
|
||||
{
|
||||
$x = $y = 0;
|
||||
$lettercount = 0;
|
||||
$cell = str_replace([' ', '\t', '\r', '\n', '\v', '\0', '$'], '', $cell);
|
||||
if (empty($cell)) {
|
||||
return [];
|
||||
}
|
||||
$cell = strtoupper($cell);
|
||||
for ($i = 0, $len = strlen($cell); $i < $len; $i++) {
|
||||
if ($cell[$i] >= 'A' && $cell[$i] <= 'Z') {
|
||||
$lettercount++;
|
||||
}
|
||||
}
|
||||
if ($lettercount > 0) {
|
||||
$x = ord($cell[$lettercount - 1]) - ord('A');
|
||||
$e = 1;
|
||||
for ($i = $lettercount - 2; $i >= 0; $i--) {
|
||||
$x += (ord($cell[$i]) - ord('A') + 1) * (26 ** $e);
|
||||
$e++;
|
||||
}
|
||||
$x++; //to make 1-based
|
||||
}
|
||||
if ($lettercount < strlen($cell)) {
|
||||
$y = ((int)substr($cell, $lettercount));
|
||||
}
|
||||
return [$y, $x];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert R1C1 cell reference format (row/col 1-based) to A1 cell reference format
|
||||
*
|
||||
* @param integer $y Row number (starting from 1) or a 2-element integer array with cell reference.
|
||||
* @param integer $x Optional. Column number (starting from 1). Not used if $y is an array.
|
||||
*
|
||||
* @return string Cell reference in A1 format
|
||||
*/
|
||||
public static function coord2cell($y, $x = null)
|
||||
{
|
||||
if (is_array($y)) {
|
||||
$x = $y[1];
|
||||
$y = $y[0];
|
||||
}
|
||||
$c = '';
|
||||
for ($i = $x - 1; $i >= 0; $i = ((int)($i / 26)) - 1) {
|
||||
$c = chr(ord('A') + $i % 26) . $c;
|
||||
}
|
||||
return $c . strval($y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert A1 range reference format to R1C1 range reference format
|
||||
*
|
||||
* @param string $range Range reference in A1 format
|
||||
*
|
||||
* @return array Range reference in R1C1 format as a 4-element integer array: [top-left row, top-left col, bottom-right row, bottom-right col]
|
||||
*/
|
||||
public static function range2coord($range)
|
||||
{
|
||||
$temp = explode(':', $range);
|
||||
if (empty($temp[1])) return self::cell2coord($temp[0]);
|
||||
return array_merge(self::cell2coord($temp[0]), self::cell2coord($temp[1]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert R1C1 range reference format (row/col 1-based) to A1 range reference format
|
||||
*
|
||||
* Possible parameters:
|
||||
* top-left row number, top-left column number, bottom-right row number, bottom-right column number
|
||||
* [top-left row number, top-left column number], [bottom-right row number, bottom-right column number]
|
||||
* [top-left row number, top-left column number, bottom-right row number, bottom-right column number]
|
||||
*
|
||||
* @return string Range reference in A1 format
|
||||
*/
|
||||
public static function coord2range($p1, ...$p)
|
||||
{
|
||||
switch (count($p)) {
|
||||
case 0: $yi = $p1[0]; $xi = $p1[1]; $yf = $p1[2]; $xf = $p1[3]; break;
|
||||
case 1: $yi = $p1[0]; $xi = $p1[1]; $yf = $p[0][0]; $xf = $p[0][1]; break;
|
||||
default: $yi = $p1; $xi = $p[0]; $yf = $p[1]; $xf = $p[2]; break;
|
||||
}
|
||||
return self::coord2cell($yi, $xi) . ':' . self::coord2cell($yf, $xf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change or add cell style parameters without losing the original content or previously established styles
|
||||
*
|
||||
* @param string $data Data matrix (2-dim 0-based array) where style will be modified. Passed by reference.
|
||||
* @param string|array $cell Cell to modify in A1 format (string) or R1C1 format (2 integer elements array)
|
||||
* @param array $style Associative array with style attributes name/value pairs to change/add. You can use wildcard for
|
||||
* specific borders to retain original value.
|
||||
* @param integer $edge Optional. If border attribute specified in $style, this parameter indicates on which edge is applied.
|
||||
* The unspecified edges retain their original value (or 'none' if not specified).
|
||||
* Default: all edges. You can combine (adding or or-ing) the constants EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_LEFT
|
||||
* @return void
|
||||
*/
|
||||
public static function setCellStyle(&$data, $cell, $style, $edge = self::EDGE_ALL)
|
||||
{
|
||||
if (is_string($cell)) $cell = self::cell2coord($cell);//A1 to R1C1 if needed
|
||||
if (!array_key_exists($cell[0] - 1, $data) || !array_key_exists($cell[1] - 1, $data[$cell[0] - 1])) return;//quit if cell doesn't exist
|
||||
//border processing
|
||||
if (array_key_exists('border', $style)) {
|
||||
//get original border as 4 element array
|
||||
$oldBorder = self::getTagAttributes($data[$cell[0] - 1][$cell[1] - 1], 'style', 'border');
|
||||
if (empty($oldBorder)) {
|
||||
$oldBorder = ['none', 'none', 'none', 'none']; //if border not present in original cell, create a no-border attribute
|
||||
} else {
|
||||
$oldBorder = explode(' ', $oldBorder);
|
||||
if (count($oldBorder) == 1) $oldBorder = [$oldBorder[0], $oldBorder[0], $oldBorder[0], $oldBorder[0]];
|
||||
}
|
||||
//get desired border as 4 element array
|
||||
$border = explode(' ', $style['border']);
|
||||
if (count($border) == 1) $border = [$border[0], $border[0], $border[0], $border[0]];
|
||||
//new border copied from old one
|
||||
$newBorder = $oldBorder;
|
||||
//set border as indicated by $edge, except when $border has wildcard
|
||||
if ($edge & self::EDGE_TOP && $border[0] != '*') $newBorder[0] = $border[0];
|
||||
if ($edge & self::EDGE_RIGHT && $border[1] != '*') $newBorder[1] = $border[1];
|
||||
if ($edge & self::EDGE_BOTTOM && $border[2] != '*') $newBorder[2] = $border[2];
|
||||
if ($edge & self::EDGE_LEFT && $border[3] != '*') $newBorder[3] = $border[3];
|
||||
//implode to the new border
|
||||
$style['border'] = implode(' ', $newBorder);
|
||||
}
|
||||
$data[$cell[0] - 1][$cell[1] - 1] = self::setTagAttributes($data[$cell[0] - 1][$cell[1] - 1], 'style', $style);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply style to a sheet range. Border style are only applied to edges of range.
|
||||
*
|
||||
* @param array $data Data matrix (2-dim 0-based array) where style will be modified. Passed by reference.
|
||||
* @param string|array $range Range to modify in A1 format (string) or R1C1 format (4-element integer array)
|
||||
* @param array $style Associative array with style attributes name/value pairs to change
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setRangeStyle(&$data, $range, $style)
|
||||
{
|
||||
if (is_string($range)) $range = self::range2coord($range);
|
||||
// 1 cell range
|
||||
if (!isset($range[2])) return self::setCellStyle($data, $range, $style); //1 cell case
|
||||
// Edge of range
|
||||
self::setCellStyle($data, [$range[0], $range[1]], $style, self::EDGE_TOP + self::EDGE_LEFT); //top-left corner
|
||||
for ($i = $range[1] + 1; $i < $range[3]; $i++) self::setCellStyle($data, [$range[0], $i], $style, self::EDGE_TOP); //loop top edge (except first and last column)
|
||||
self::setCellStyle($data, [$range[0], $range[3]], $style, self::EDGE_TOP + self::EDGE_RIGHT); //top-right corner
|
||||
for ($i = $range[0] + 1; $i < $range[2]; $i++) self::setCellStyle($data, [$i, $range[3]], $style, self::EDGE_RIGHT); //loop right edge (except top and bottom row)
|
||||
self::setCellStyle($data, [$range[2], $range[3]], $style, self::EDGE_BOTTOM + self::EDGE_RIGHT); //bottom-right corner
|
||||
for ($i = $range[3] - 1; $i > $range[1]; $i--) self::setCellStyle($data, [$range[2], $i], $style, self::EDGE_BOTTOM); //loop bottom edge (except right and left column)
|
||||
self::setCellStyle($data, [$range[2], $range[1]], $style, self::EDGE_BOTTOM + self::EDGE_LEFT); //bottom-left corner
|
||||
for ($i = $range[2] - 1; $i > $range[0]; $i--) self::setCellStyle($data, [$i, $range[1]], $style, self::EDGE_LEFT); //loop left edge (except bottom and top row)
|
||||
// Internal cells
|
||||
if (array_key_exists('border', $style)) unset($style['border']);
|
||||
for ($i = $range[0] + 1; $i < $range[2]; $i++) {
|
||||
for ($j = $range[1] + 1; $j < $range[3]; $j++) {
|
||||
self::setCellStyle($data, [$i, $j], $style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tag attributes from a HTML-style tag as an associative array with attributes name/value pairs.
|
||||
* If $attribute specified, get value of the specified attribute.
|
||||
*
|
||||
* @param string $str Text to extract tag attributes
|
||||
* @param string $tag The tag to look for
|
||||
* @param string $attribute Optional. Get the value of specified attribute. Default: get all attributes name/value as an array.
|
||||
*
|
||||
* @return array|string Attributes name/value as an array ($attribute == '') or attribute value as a string ($attribute != '').
|
||||
*/
|
||||
public static function getTagAttributes($str, $tag, $attribute = '')
|
||||
{
|
||||
$attributes = ($attribute === '') ? [] : '';
|
||||
if (preg_match("/<{$tag}\s+([^>]+)(?:>)/i", $str, $m)) {
|
||||
$tagcontent = $m[1];
|
||||
if (preg_match_all('/([a-z0-9\-_]+)=(["\'])(.*?)\2/si', $tagcontent, $m, PREG_SET_ORDER)) {
|
||||
foreach ($m as $match) {
|
||||
if ($attribute !== '') {
|
||||
if ($attribute === $match[1]) return $match[3];
|
||||
} else {
|
||||
$attributes[$match[1]] = $match[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change HTML-style tag attributes. If tag doesn't exist, is created surrounding the previous content.
|
||||
*
|
||||
* @param string $str Text to modify
|
||||
* @param string $tag Tag in text to modify
|
||||
* @param array $attributes Associative array with attribute name/value pairs
|
||||
*
|
||||
* @return string Modified text
|
||||
*/
|
||||
public static function setTagAttributes($str, $tag, $attributes)
|
||||
{
|
||||
if (empty($attributes)) return $str;
|
||||
$opening = "<{$tag}";
|
||||
if (preg_match("/(<{$tag}\s*[^>]*)(>.*<\/{$tag}>)/i", $str, $m)) {
|
||||
$stropen = $m[1];
|
||||
} else {
|
||||
$str = "<{$tag}>{$str}</{$tag}>";
|
||||
$stropen = $opening;
|
||||
}
|
||||
//merge original attributes and specified attributes (replacing if necessary)
|
||||
$attributes = array_merge(self::getTagAttributes($str, $tag), $attributes);
|
||||
//add attributes to tag
|
||||
foreach ($attributes as $attr => $value) $opening .= " {$attr}=\"{$value}\"";
|
||||
//replace original tag (with attributes) with the new one
|
||||
$str = substr_replace($str, $opening, strpos($str, $stropen), strlen($stropen));
|
||||
return $str;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user