mirror of
https://github.com/shuchkin/simplexlsxgen.git
synced 2023-08-10 21:12:59 +03:00
Minor corrections and new features
This commit is contained in:
parent
306aa7dad9
commit
5d14683e22
@ -136,7 +136,8 @@ exit();
|
|||||||
$xlsx->autoFilter('A1:B10');
|
$xlsx->autoFilter('A1:B10');
|
||||||
|
|
||||||
// Freeze rows and columns from top-left corner up to, but not including,
|
// Freeze rows and columns from top-left corner up to, but not including,
|
||||||
// the row and column of the indicated cell
|
// the row and column of the indicated cell (in A1 format as string or
|
||||||
|
// R1C1 format as 2-element integer array)
|
||||||
$xlsx->freezePanes('C3');
|
$xlsx->freezePanes('C3');
|
||||||
|
|
||||||
// RTL mode
|
// RTL mode
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace Shuchkin;
|
||||||
|
|
||||||
/** @noinspection ReturnTypeCanBeDeclaredInspection */
|
/** @noinspection ReturnTypeCanBeDeclaredInspection */
|
||||||
/** @noinspection PhpMissingReturnTypeInspection */
|
/** @noinspection PhpMissingReturnTypeInspection */
|
||||||
/** @noinspection NullCoalescingOperatorCanBeUsedInspection */
|
/** @noinspection NullCoalescingOperatorCanBeUsedInspection */
|
||||||
|
|
||||||
/** @noinspection PhpIssetCanBeReplacedWithCoalesceInspection */
|
/** @noinspection PhpIssetCanBeReplacedWithCoalesceInspection */
|
||||||
|
|
||||||
namespace Shuchkin;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class SimpleXLSXGen
|
* Class SimpleXLSXGen
|
||||||
* Export data to MS Excel. PHP XLSX generator
|
*
|
||||||
* Author: sergey.shuchkin@gmail.com
|
* Export data to MS Excel. PHP XLSX generator.
|
||||||
|
*
|
||||||
|
* @author Sergey Shuchkin <sergey.shuchkin@gmail.com>
|
||||||
*/
|
*/
|
||||||
class SimpleXLSXGen
|
class SimpleXLSXGen
|
||||||
{
|
{
|
||||||
@ -40,6 +40,7 @@ class SimpleXLSXGen
|
|||||||
protected $keywords;
|
protected $keywords;
|
||||||
protected $category;
|
protected $category;
|
||||||
protected $lastModifiedBy;
|
protected $lastModifiedBy;
|
||||||
|
|
||||||
const N_NORMAL = 0; // General
|
const N_NORMAL = 0; // General
|
||||||
const N_INT = 1; // 0
|
const N_INT = 1; // 0
|
||||||
const N_DEC = 2; // 0.00
|
const N_DEC = 2; // 0.00
|
||||||
@ -89,6 +90,8 @@ class SimpleXLSXGen
|
|||||||
const B_MEDIUM_DASH_DOT_DOT = 12;
|
const B_MEDIUM_DASH_DOT_DOT = 12;
|
||||||
const B_SLANT_DASH_DOT = 13;
|
const B_SLANT_DASH_DOT = 13;
|
||||||
|
|
||||||
|
const NEWLINE = "\r\n";
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->subject = '';
|
$this->subject = '';
|
||||||
@ -142,78 +145,83 @@ class SimpleXLSXGen
|
|||||||
];
|
];
|
||||||
$this->XF_KEYS[implode('-', $this->XF[0])] = 0; // & keys
|
$this->XF_KEYS[implode('-', $this->XF[0])] = 0; // & keys
|
||||||
$this->XF_KEYS[implode('-', $this->XF[1])] = 1;
|
$this->XF_KEYS[implode('-', $this->XF[1])] = 1;
|
||||||
|
|
||||||
$this->template = [
|
$this->template = [
|
||||||
'_rels/.rels' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
'_rels/.rels' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . self::NEWLINE .
|
||||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' . self::NEWLINE .
|
||||||
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
|
'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>' . self::NEWLINE .
|
||||||
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
|
'<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>' . self::NEWLINE .
|
||||||
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
|
'<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>' . self::NEWLINE .
|
||||||
</Relationships>',
|
'</Relationships>',
|
||||||
'docProps/app.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
'docProps/app.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . self::NEWLINE .
|
||||||
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">
|
'<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">' . self::NEWLINE .
|
||||||
<TotalTime>0</TotalTime>
|
'<TotalTime>0</TotalTime>' . self::NEWLINE .
|
||||||
<Application>{APP}</Application>
|
'<Application>{APP}</Application>' . self::NEWLINE .
|
||||||
<Company>{COMPANY}</Company>
|
'<Company>{COMPANY}</Company>' . self::NEWLINE .
|
||||||
<Manager>{MANAGER}</Manager>
|
'<Manager>{MANAGER}</Manager>' . self::NEWLINE .
|
||||||
</Properties>',
|
'</Properties>',
|
||||||
'docProps/core.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
'docProps/core.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . self::NEWLINE .
|
||||||
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
'<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' . self::NEWLINE .
|
||||||
<dcterms:created xsi:type="dcterms:W3CDTF">{DATE}</dcterms:created>
|
'<dcterms:created xsi:type="dcterms:W3CDTF">{DATE}</dcterms:created>' . self::NEWLINE .
|
||||||
<dc:title>{TITLE}</dc:title>
|
'<dc:title>{TITLE}</dc:title>' . self::NEWLINE .
|
||||||
<dc:subject>{SUBJECT}</dc:subject>
|
'<dc:subject>{SUBJECT}</dc:subject>' . self::NEWLINE .
|
||||||
<dc:creator>{AUTHOR}</dc:creator>
|
'<dc:creator>{AUTHOR}</dc:creator>' . self::NEWLINE .
|
||||||
<cp:lastModifiedBy>{LAST_MODIFY_BY}</cp:lastModifiedBy>
|
'<cp:lastModifiedBy>{LAST_MODIFY_BY}</cp:lastModifiedBy>' . self::NEWLINE .
|
||||||
<cp:keywords>{KEYWORD}</cp:keywords>
|
'<cp:keywords>{KEYWORD}</cp:keywords>' . self::NEWLINE .
|
||||||
<dc:description>{DESCRIPTION}</dc:description>
|
'<dc:description>{DESCRIPTION}</dc:description>' . self::NEWLINE .
|
||||||
<cp:category>{CATEGORY}</cp:category>
|
'<cp:category>{CATEGORY}</cp:category>' . self::NEWLINE .
|
||||||
<dc:language>en-US</dc:language>
|
'<dc:language>en-US</dc:language>' . self::NEWLINE .
|
||||||
<dcterms:modified xsi:type="dcterms:W3CDTF">{DATE}</dcterms:modified>
|
'<dcterms:modified xsi:type="dcterms:W3CDTF">{DATE}</dcterms:modified>' . self::NEWLINE .
|
||||||
<cp:revision>1</cp:revision>
|
'<cp:revision>1</cp:revision>' . self::NEWLINE .
|
||||||
</cp:coreProperties>',
|
'</cp:coreProperties>',
|
||||||
'xl/_rels/workbook.xml.rels' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
'xl/_rels/workbook.xml.rels' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . self::NEWLINE .
|
||||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' . self::NEWLINE .
|
||||||
{RELS}
|
'{RELS}' . self::NEWLINE .
|
||||||
</Relationships>',
|
'</Relationships>',
|
||||||
'xl/worksheets/sheet1.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
'xl/worksheets/sheet1.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . self::NEWLINE .
|
||||||
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
'<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">' . self::NEWLINE .
|
||||||
<dimension ref="{REF}"/>
|
'<dimension ref="{REF}"/>' . self::NEWLINE .
|
||||||
{SHEETVIEWS}
|
'{SHEETVIEWS}' . self::NEWLINE .
|
||||||
{COLS}
|
'{COLS}' . self::NEWLINE .
|
||||||
<sheetData>{ROWS}</sheetData>
|
'<sheetData>' . self::NEWLINE . '{ROWS}' . self::NEWLINE . '</sheetData>' . self::NEWLINE .
|
||||||
{AUTOFILTER}{MERGECELLS}{HYPERLINKS}
|
'{AUTOFILTER}' . '{MERGECELLS}' . self::NEWLINE . '{HYPERLINKS}' . self::NEWLINE .
|
||||||
</worksheet>',
|
'</worksheet>',
|
||||||
'xl/worksheets/_rels/sheet1.xml.rels' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
'xl/worksheets/_rels/sheet1.xml.rels' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . self::NEWLINE .
|
||||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">{HYPERLINKS}</Relationships>',
|
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' . self::NEWLINE .
|
||||||
'xl/sharedStrings.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
'{HYPERLINKS}' . self::NEWLINE .
|
||||||
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="{CNT}" uniqueCount="{CNT}">{STRINGS}</sst>',
|
'</Relationships>',
|
||||||
'xl/styles.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
'xl/sharedStrings.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . self::NEWLINE .
|
||||||
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
|
'<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="{CNT}" uniqueCount="{CNT}">' . self::NEWLINE .
|
||||||
{NUMFMTS}
|
'{STRINGS}' . self::NEWLINE .
|
||||||
{FONTS}
|
'</sst>',
|
||||||
{FILLS}
|
'xl/styles.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . self::NEWLINE .
|
||||||
{BORDERS}
|
'<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">' . self::NEWLINE .
|
||||||
<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" /></cellStyleXfs>
|
'{NUMFMTS}' . self::NEWLINE .
|
||||||
{XF}
|
'{FONTS}' . self::NEWLINE .
|
||||||
<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>
|
'{FILLS}' . self::NEWLINE .
|
||||||
</styleSheet>',
|
'{BORDERS}' . self::NEWLINE .
|
||||||
'xl/workbook.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
'<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" /></cellStyleXfs>' . self::NEWLINE .
|
||||||
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
'{XF}' . self::NEWLINE .
|
||||||
<fileVersion appName="{APP}"/>
|
'<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>' . self::NEWLINE .
|
||||||
<sheets>
|
'</styleSheet>',
|
||||||
{SHEETS}
|
'xl/workbook.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . self::NEWLINE .
|
||||||
</sheets>
|
'<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">' . self::NEWLINE .
|
||||||
</workbook>',
|
'<fileVersion appName="{APP}"/>' . self::NEWLINE .
|
||||||
'[Content_Types].xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
'<sheets>' . self::NEWLINE .
|
||||||
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
'{SHEETS}' . self::NEWLINE .
|
||||||
<Override PartName="/rels/.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
'</sheets>' . self::NEWLINE .
|
||||||
<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
|
'</workbook>',
|
||||||
<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
|
'[Content_Types].xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . self::NEWLINE .
|
||||||
<Override PartName="/xl/_rels/workbook.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
'<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">' . self::NEWLINE .
|
||||||
<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
|
'<Override PartName="/rels/.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>' . self::NEWLINE .
|
||||||
<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
|
'<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>' . self::NEWLINE .
|
||||||
<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
|
'<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>' . self::NEWLINE .
|
||||||
{TYPES}
|
'<Override PartName="/xl/_rels/workbook.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>' . self::NEWLINE .
|
||||||
</Types>',
|
'<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>' . self::NEWLINE .
|
||||||
|
'<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>' . self::NEWLINE .
|
||||||
|
'<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>' . self::NEWLINE .
|
||||||
|
'{TYPES}' . self::NEWLINE .
|
||||||
|
'</Types>',
|
||||||
];
|
];
|
||||||
// <col min="1" max="1" width="22.1796875" bestFit="1" customWidth="1"/>
|
// <col min="1" max="1" width="22.1796875" bestFit="1" customWidth="1"/>
|
||||||
// <row r="1" spans="1:2" x14ac:dyDescent="0.35"><c r="A1" t="s"><v>0</v></c><c r="B1"><v>100</v></c></row><row r="2" spans="1:2" x14ac:dyDescent="0.35"><c r="A2" t="s"><v>1</v></c><c r="B2"><v>200</v></c></row>
|
// <row r="1" spans="1:2" x14ac:dyDescent="0.35"><c r="A1" t="s"><v>0</v></c><c r="B1"><v>100</v></c></row><row r="2" spans="1:2" x14ac:dyDescent="0.35"><c r="A2" t="s"><v>1</v></c><c r="B2"><v>200</v></c></row>
|
||||||
@ -311,7 +319,8 @@ class SimpleXLSXGen
|
|||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
}
|
}
|
||||||
fseek($fh, 0);
|
fseek($fh, 0);
|
||||||
fpassthru($fh);
|
if (function_exists('fpassthru')) fpassthru($fh);
|
||||||
|
else echo stream_get_contents($fh);
|
||||||
fclose($fh);
|
fclose($fh);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -331,11 +340,10 @@ class SimpleXLSXGen
|
|||||||
$s = '';
|
$s = '';
|
||||||
for ($i = 0; $i < $cnt_sheets; $i++) {
|
for ($i = 0; $i < $cnt_sheets; $i++) {
|
||||||
$s .= '<Relationship Id="rId' . ($i + 1) . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"' .
|
$s .= '<Relationship Id="rId' . ($i + 1) . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"' .
|
||||||
' Target="worksheets/sheet' . ($i + 1) . ".xml\"/>\r\n";
|
' Target="worksheets/sheet' . ($i + 1) . '.xml"/>' . self::NEWLINE;
|
||||||
}
|
}
|
||||||
$s .= '<Relationship Id="rId' . ($i + 1) . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>' . "\r\n";
|
$s .= '<Relationship Id="rId' . ($i + 1) . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>' . self::NEWLINE;
|
||||||
$s .= '<Relationship Id="rId' . ($i + 2) . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>';
|
$s .= '<Relationship Id="rId' . ($i + 2) . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>';
|
||||||
|
|
||||||
$template = str_replace('{RELS}', $s, $template);
|
$template = str_replace('{RELS}', $s, $template);
|
||||||
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
|
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
|
||||||
$entries++;
|
$entries++;
|
||||||
@ -344,30 +352,38 @@ class SimpleXLSXGen
|
|||||||
foreach ($this->sheets as $k => $v) {
|
foreach ($this->sheets as $k => $v) {
|
||||||
$s .= '<sheet name="' . $this->esc($v['name']) . '" sheetId="' . ($k + 1) . '" r:id="rId' . ($k + 1) . '"/>';
|
$s .= '<sheet name="' . $this->esc($v['name']) . '" sheetId="' . ($k + 1) . '" r:id="rId' . ($k + 1) . '"/>';
|
||||||
}
|
}
|
||||||
$search = ['{SHEETS}', '{APP}'];
|
$template = str_replace(
|
||||||
$replace = [$s, $this->esc($this->application)];
|
['{SHEETS}', '{APP}'],
|
||||||
$template = str_replace($search, $replace, $template);
|
[$s, $this->esc($this->application)],
|
||||||
|
$template
|
||||||
|
);
|
||||||
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
|
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
|
||||||
$entries++;
|
$entries++;
|
||||||
} elseif ($cfilename === 'docProps/app.xml') {
|
} elseif ($cfilename === 'docProps/app.xml') {
|
||||||
$search = ['{APP}', '{COMPANY}', '{MANAGER}'];
|
$template = str_replace(
|
||||||
$replace = [$this->esc($this->application), $this->esc($this->company), $this->esc($this->manager)];
|
['{APP}', '{COMPANY}', '{MANAGER}'],
|
||||||
$template = str_replace($search, $replace, $template);
|
[$this->esc($this->application), $this->esc($this->company), $this->esc($this->manager)],
|
||||||
|
$template
|
||||||
|
);
|
||||||
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
|
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
|
||||||
$entries++;
|
$entries++;
|
||||||
} elseif ($cfilename === 'docProps/core.xml') {
|
} elseif ($cfilename === 'docProps/core.xml') {
|
||||||
$search = ['{DATE}', '{AUTHOR}', '{TITLE}', '{SUBJECT}', '{KEYWORD}', '{DESCRIPTION}', '{CATEGORY}', '{LAST_MODIFY_BY}'];
|
$template = str_replace(
|
||||||
$replace = [gmdate('Y-m-d\TH:i:s\Z'), $this->esc($this->author), $this->esc($this->title), $this->esc($this->subject), $this->esc($this->keywords), $this->esc($this->description), $this->esc($this->category), $this->esc($this->lastModifiedBy)];
|
['{DATE}', '{AUTHOR}', '{TITLE}', '{SUBJECT}', '{KEYWORD}', '{DESCRIPTION}', '{CATEGORY}', '{LAST_MODIFY_BY}'],
|
||||||
$template = str_replace($search, $replace, $template);
|
[gmdate('Y-m-d\TH:i:s\Z'), $this->esc($this->author), $this->esc($this->title), $this->esc($this->subject), $this->esc($this->keywords), $this->esc($this->description), $this->esc($this->category), $this->esc($this->lastModifiedBy)],
|
||||||
|
$template
|
||||||
|
);
|
||||||
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
|
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
|
||||||
$entries++;
|
$entries++;
|
||||||
} elseif ($cfilename === 'xl/sharedStrings.xml') {
|
} elseif ($cfilename === 'xl/sharedStrings.xml') {
|
||||||
if (!count($this->SI)) {
|
if (!count($this->SI)) {
|
||||||
$this->SI[] = 'No Data';
|
$this->SI[] = 'No Data';
|
||||||
}
|
}
|
||||||
$si_cnt = count($this->SI);
|
$template = str_replace(
|
||||||
$si = '<si><t>' . implode("</t></si>\r\n<si><t>", $this->SI) . '</t></si>';
|
['{CNT}', '{STRINGS}'],
|
||||||
$template = str_replace(['{CNT}', '{STRINGS}'], [$si_cnt, $si], $template);
|
[count($this->SI), '<si><t>' . implode('</t></si>' . self::NEWLINE . '<si><t>', $this->SI) . '</t></si>'],
|
||||||
|
$template
|
||||||
|
);
|
||||||
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
|
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
|
||||||
$entries++;
|
$entries++;
|
||||||
} elseif ($cfilename === 'xl/worksheets/sheet1.xml') {
|
} elseif ($cfilename === 'xl/worksheets/sheet1.xml') {
|
||||||
@ -385,10 +401,10 @@ class SimpleXLSXGen
|
|||||||
$filename = 'xl/worksheets/_rels/sheet' . ($k + 1) . '.xml.rels';
|
$filename = 'xl/worksheets/_rels/sheet' . ($k + 1) . '.xml.rels';
|
||||||
foreach ($v['hyperlinks'] as $h) {
|
foreach ($v['hyperlinks'] as $h) {
|
||||||
if ($h['ID']) {
|
if ($h['ID']) {
|
||||||
$RH[] = '<Relationship Id="' . $h['ID'] . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="' . $this->esc($h['H']) . '" TargetMode="External"/>';
|
$RH[] = ' <Relationship Id="' . $h['ID'] . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="' . $this->esc($h['H']) . '" TargetMode="External"/>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$xml = str_replace('{HYPERLINKS}', implode("\r\n", $RH), $template);
|
$xml = str_replace('{HYPERLINKS}', implode(self::NEWLINE, $RH), $template);
|
||||||
$this->_writeEntry($fh, $cdrec, $filename, $xml);
|
$this->_writeEntry($fh, $cdrec, $filename, $xml);
|
||||||
$entries++;
|
$entries++;
|
||||||
}
|
}
|
||||||
@ -402,7 +418,7 @@ class SimpleXLSXGen
|
|||||||
$TYPES[] = '<Override PartName="/xl/worksheets/_rels/sheet' . ($k + 1) . '.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
|
$TYPES[] = '<Override PartName="/xl/worksheets/_rels/sheet' . ($k + 1) . '.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$template = str_replace('{TYPES}', implode("\r\n", $TYPES), $template);
|
$template = str_replace('{TYPES}', implode(self::NEWLINE, $TYPES), $template);
|
||||||
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
|
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
|
||||||
$entries++;
|
$entries++;
|
||||||
} elseif ($cfilename === 'xl/styles.xml') {
|
} elseif ($cfilename === 'xl/styles.xml') {
|
||||||
@ -465,7 +481,6 @@ class SimpleXLSXGen
|
|||||||
if ($xf[1] & self::A_WRAPTEXT) {
|
if ($xf[1] & self::A_WRAPTEXT) {
|
||||||
$align .= ' wrapText="1"';
|
$align .= ' wrapText="1"';
|
||||||
}
|
}
|
||||||
|
|
||||||
// border
|
// border
|
||||||
$BR_ID = 0;
|
$BR_ID = 0;
|
||||||
if ($xf[6] !== '') {
|
if ($xf[6] !== '') {
|
||||||
@ -534,7 +549,7 @@ class SimpleXLSXGen
|
|||||||
|
|
||||||
$template = str_replace(
|
$template = str_replace(
|
||||||
['{NUMFMTS}', '{FONTS}', '{XF}', '{FILLS}', '{BORDERS}'],
|
['{NUMFMTS}', '{FONTS}', '{XF}', '{FILLS}', '{BORDERS}'],
|
||||||
[implode("\r\n", $NF), implode("\r\n", $FONTS), implode("\r\n", $XF), implode("\r\n", $FILLS), implode("\r\n", $BR)],
|
[implode(self::NEWLINE, $NF), implode(self::NEWLINE, $FONTS), implode(self::NEWLINE, $XF), implode(self::NEWLINE, $FILLS), implode(self::NEWLINE, $BR)],
|
||||||
$template
|
$template
|
||||||
);
|
);
|
||||||
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
|
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
|
||||||
@ -556,7 +571,6 @@ class SimpleXLSXGen
|
|||||||
fwrite($fh, pack('V', $before_cd)); // offset to start of central dir
|
fwrite($fh, pack('V', $before_cd)); // offset to start of central dir
|
||||||
fwrite($fh, pack('v', mb_strlen($zipComments, '8bit'))); // .zip file comment length
|
fwrite($fh, pack('v', mb_strlen($zipComments, '8bit'))); // .zip file comment length
|
||||||
fwrite($fh, $zipComments);
|
fwrite($fh, $zipComments);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,24 +656,19 @@ class SimpleXLSXGen
|
|||||||
setlocale(LC_NUMERIC, 'C');
|
setlocale(LC_NUMERIC, 'C');
|
||||||
$COLS = [];
|
$COLS = [];
|
||||||
$ROWS = [];
|
$ROWS = [];
|
||||||
// $SHEETVIEWS = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"'.($this->rtl ? ' rightToLeft="1"' : '').'>';
|
|
||||||
$SHEETVIEWS = '';
|
$SHEETVIEWS = '';
|
||||||
$PANE = '';
|
$PANE = '';
|
||||||
if (count($this->sheets[$idx]['rows'])) {
|
if (count($this->sheets[$idx]['rows'])) {
|
||||||
if ($this->sheets[$idx]['frozen'] !== '' || isset($this->sheets[$idx]['frozen'][0]) || isset($this->sheets[$idx]['frozen'][1])) {
|
if ($this->sheets[$idx]['frozen'] !== '' || isset($this->sheets[$idx]['frozen'][0]) || isset($this->sheets[$idx]['frozen'][1])) {
|
||||||
// $AC = 'A1'; // Active Cell
|
if (is_string($this->sheets[$idx]['frozen'])) { //A1 format -> store ($AC) and convert to R1C1 0-based ($y/$x)
|
||||||
$x = $y = 0;
|
|
||||||
if (is_string($this->sheets[$idx]['frozen'])) {
|
|
||||||
$AC = $this->sheets[$idx]['frozen'];
|
$AC = $this->sheets[$idx]['frozen'];
|
||||||
self::cell2coord($AC, $x, $y);
|
[$y, $x] = self::cell2coord($AC);
|
||||||
} else {
|
$y--;
|
||||||
if (isset($this->sheets[$idx]['frozen'][0])) {
|
$x--;
|
||||||
$x = $this->sheets[$idx]['frozen'][0];
|
} else { //R1C1 1-based format -> store in R1C1 0-based ($y/$x) and convert to A1 format ($AC)
|
||||||
}
|
$y = $this->sheets[$idx]['frozen'][0] - 1;
|
||||||
if (isset($this->sheets[$idx]['frozen'][1])) {
|
$x = $this->sheets[$idx]['frozen'][1] - 1;
|
||||||
$y = $this->sheets[$idx]['frozen'][1];
|
$AC = self::coord2cell($this->sheets[$idx]['frozen']);
|
||||||
}
|
|
||||||
$AC = self::coord2cell($x, $y);
|
|
||||||
}
|
}
|
||||||
if ($x > 0 || $y > 0) {
|
if ($x > 0 || $y > 0) {
|
||||||
$split = '';
|
$split = '';
|
||||||
@ -681,10 +690,9 @@ class SimpleXLSXGen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->rtl || $PANE) {
|
if ($this->rtl || $PANE) {
|
||||||
$SHEETVIEWS .= '<sheetViews>
|
$SHEETVIEWS .= '<sheetViews>' . self::NEWLINE . '<sheetView workbookViewId="0"' . ($this->rtl ? ' rightToLeft="1"' : '');
|
||||||
<sheetView workbookViewId="0"' . ($this->rtl ? ' rightToLeft="1"' : '');
|
$SHEETVIEWS .= $PANE ? ('>' . self::NEWLINE . $PANE . self::NEWLINE . '</sheetView>') : ' />';
|
||||||
$SHEETVIEWS .= $PANE ? ">\r\n" . $PANE . "\r\n</sheetView>" : ' />';
|
$SHEETVIEWS .= self::NEWLINE . '</sheetViews>';
|
||||||
$SHEETVIEWS .= "\r\n</sheetViews>";
|
|
||||||
}
|
}
|
||||||
$COLS[] = '<cols>';
|
$COLS[] = '<cols>';
|
||||||
$CUR_ROW = 0;
|
$CUR_ROW = 0;
|
||||||
@ -701,7 +709,7 @@ class SimpleXLSXGen
|
|||||||
}
|
}
|
||||||
$cname = $this->num2name($CUR_COL) . $CUR_ROW;
|
$cname = $this->num2name($CUR_COL) . $CUR_ROW;
|
||||||
if ($v === null || $v === '') {
|
if ($v === null || $v === '') {
|
||||||
$row .= '<c r="' . $cname . '"/>';
|
$row .= ' <c r="' . $cname . '"/>';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$ct = $cv = $cf = null;
|
$ct = $cv = $cf = null;
|
||||||
@ -918,15 +926,15 @@ class SimpleXLSXGen
|
|||||||
$this->XF[] = [$N, $A, $F, $FL, $C, $BG, $BR, $FS];
|
$this->XF[] = [$N, $A, $F, $FL, $C, $BG, $BR, $FS];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$row .= '<c r="' . $cname . '"' . ($ct ? ' t="' . $ct . '"' : '') . ($cs ? ' s="' . $cs . '"' : '') . '>'
|
$row .= ' <c r="' . $cname . '"' . ($ct ? ' t="' . $ct . '"' : '') . ($cs ? ' s="' . $cs . '"' : '') . '>'
|
||||||
. ($cf ? '<f>' . $cf . '</f>' : '')
|
. ($cf ? '<f>' . $cf . '</f>' : '')
|
||||||
. ($ct === 'inlineStr' ? '<is><t>' . $cv . '</t></is>' : '<v>' . $cv . '</v>') . "</c>\r\n";
|
. ($ct === 'inlineStr' ? '<is><t>' . $cv . '</t></is>' : '<v>' . $cv . '</v>') . '</c>' . self::NEWLINE;
|
||||||
}
|
}
|
||||||
$ROWS[] = '<row r="' . $CUR_ROW . '"' . ($RH ? ' customHeight="1" ht="' . $RH . '"' : '') . '>' . $row . "</row>";
|
$ROWS[] = ' <row r="' . $CUR_ROW . '"' . ($RH ? ' customHeight="1" ht="' . $RH . '"' : '') . '>' . self::NEWLINE . $row . ' </row>';
|
||||||
}
|
}
|
||||||
foreach ($COL as $k => $max) {
|
foreach ($COL as $k => $max) {
|
||||||
$w = isset($this->sheets[$idx]['colwidth'][$k]) ? $this->sheets[$idx]['colwidth'][$k] : min($max + 1, 60);
|
$w = isset($this->sheets[$idx]['colwidth'][$k]) ? $this->sheets[$idx]['colwidth'][$k] : min($max + 1, 60);
|
||||||
$COLS[] = '<col min="' . $k . '" max="' . $k . '" width="' . $w . '" customWidth="1" />';
|
$COLS[] = ' <col min="' . $k . '" max="' . $k . '" width="' . $w . '" customWidth="1" />';
|
||||||
}
|
}
|
||||||
$COLS[] = '</cols>';
|
$COLS[] = '</cols>';
|
||||||
$REF = 'A1:' . $this->num2name(count($COL)) . $CUR_ROW;
|
$REF = 'A1:' . $this->num2name(count($COL)) . $CUR_ROW;
|
||||||
@ -937,15 +945,15 @@ class SimpleXLSXGen
|
|||||||
|
|
||||||
$AUTOFILTER = '';
|
$AUTOFILTER = '';
|
||||||
if ($this->sheets[$idx]['autofilter']) {
|
if ($this->sheets[$idx]['autofilter']) {
|
||||||
$AUTOFILTER = '<autoFilter ref="' . $this->sheets[$idx]['autofilter'] . '" />';
|
$AUTOFILTER = '<autoFilter ref="' . $this->sheets[$idx]['autofilter'] . '"/>' . self::NEWLINE;
|
||||||
}
|
}
|
||||||
|
|
||||||
$MERGECELLS = [];
|
$MERGECELLS = [];
|
||||||
if (count($this->sheets[$idx]['mergecells'])) {
|
if (count($this->sheets[$idx]['mergecells'])) {
|
||||||
$MERGECELLS[] = '';
|
//$MERGECELLS[] = '';
|
||||||
$MERGECELLS[] = '<mergeCells count="' . count($this->sheets[$idx]['mergecells']) . '">';
|
$MERGECELLS[] = '<mergeCells count="' . count($this->sheets[$idx]['mergecells']) . '">';
|
||||||
foreach ($this->sheets[$idx]['mergecells'] as $m) {
|
foreach ($this->sheets[$idx]['mergecells'] as $m) {
|
||||||
$MERGECELLS[] = '<mergeCell ref="' . $m . '"/>';
|
$MERGECELLS[] = ' <mergeCell ref="' . $m . '"/>';
|
||||||
}
|
}
|
||||||
$MERGECELLS[] = '</mergeCells>';
|
$MERGECELLS[] = '</mergeCells>';
|
||||||
}
|
}
|
||||||
@ -954,7 +962,7 @@ class SimpleXLSXGen
|
|||||||
if (count($this->sheets[$idx]['hyperlinks'])) {
|
if (count($this->sheets[$idx]['hyperlinks'])) {
|
||||||
$HYPERLINKS[] = '<hyperlinks>';
|
$HYPERLINKS[] = '<hyperlinks>';
|
||||||
foreach ($this->sheets[$idx]['hyperlinks'] as $h) {
|
foreach ($this->sheets[$idx]['hyperlinks'] as $h) {
|
||||||
$HYPERLINKS[] = '<hyperlink ref="' . $h['R'] . '"' . ($h['ID'] ? ' r:id="' . $h['ID'] . '"' : '') . ' location="' . $this->esc($h['L']) . '" display="' . $this->esc($h['H'] . ($h['L'] ? ' - ' . $h['L'] : '')) . '" />';
|
$HYPERLINKS[] = ' <hyperlink ref="' . $h['R'] . '"' . ($h['ID'] ? ' r:id="' . $h['ID'] . '"' : '') . ' location="' . $this->esc($h['L']) . '" display="' . $this->esc($h['H'] . ($h['L'] ? ' - ' . $h['L'] : '')) . '"/>';
|
||||||
}
|
}
|
||||||
$HYPERLINKS[] = '</hyperlinks>';
|
$HYPERLINKS[] = '</hyperlinks>';
|
||||||
}
|
}
|
||||||
@ -966,17 +974,23 @@ class SimpleXLSXGen
|
|||||||
['{REF}', '{COLS}', '{ROWS}', '{AUTOFILTER}', '{MERGECELLS}', '{HYPERLINKS}', '{SHEETVIEWS}'],
|
['{REF}', '{COLS}', '{ROWS}', '{AUTOFILTER}', '{MERGECELLS}', '{HYPERLINKS}', '{SHEETVIEWS}'],
|
||||||
[
|
[
|
||||||
$REF,
|
$REF,
|
||||||
implode("\r\n", $COLS),
|
implode(self::NEWLINE, $COLS),
|
||||||
implode("\r\n", $ROWS),
|
implode(self::NEWLINE, $ROWS),
|
||||||
$AUTOFILTER,
|
$AUTOFILTER,
|
||||||
implode("\r\n", $MERGECELLS),
|
implode(self::NEWLINE, $MERGECELLS),
|
||||||
implode("\r\n", $HYPERLINKS),
|
implode(self::NEWLINE, $HYPERLINKS),
|
||||||
$SHEETVIEWS
|
$SHEETVIEWS
|
||||||
],
|
],
|
||||||
$template
|
$template
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert column number (1, 2, ...) to column letter (A, B, ..., Z, AA, ...)
|
||||||
|
*
|
||||||
|
* @param integer $num Column number
|
||||||
|
* @return string Column letter(s)
|
||||||
|
*/
|
||||||
public function num2name($num)
|
public function num2name($num)
|
||||||
{
|
{
|
||||||
$numeric = ($num - 1) % 26;
|
$numeric = ($num - 1) % 26;
|
||||||
@ -995,9 +1009,9 @@ class SimpleXLSXGen
|
|||||||
return $excelTime;
|
return $excelTime;
|
||||||
}
|
}
|
||||||
// self::CALENDAR_WINDOWS_1900
|
// self::CALENDAR_WINDOWS_1900
|
||||||
$excel1900isLeapYear = True;
|
$excel1900isLeapYear = true;
|
||||||
if (($year === 1900) && ($month <= 2)) {
|
if (($year === 1900) && ($month <= 2)) {
|
||||||
$excel1900isLeapYear = False;
|
$excel1900isLeapYear = false;
|
||||||
}
|
}
|
||||||
$myExcelBaseDate = 2415020;
|
$myExcelBaseDate = 2415020;
|
||||||
// Julian base date Adjustment
|
// Julian base date Adjustment
|
||||||
@ -1019,13 +1033,11 @@ class SimpleXLSXGen
|
|||||||
$this->defaultFont = $name;
|
$this->defaultFont = $name;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setDefaultFontSize($size)
|
public function setDefaultFontSize($size)
|
||||||
{
|
{
|
||||||
$this->defaultFontSize = $size;
|
$this->defaultFontSize = $size;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTitle($title)
|
public function setTitle($title)
|
||||||
{
|
{
|
||||||
$this->title = $title;
|
$this->title = $title;
|
||||||
@ -1066,7 +1078,6 @@ class SimpleXLSXGen
|
|||||||
$this->category = $category;
|
$this->category = $category;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setApplication($application)
|
public function setApplication($application)
|
||||||
{
|
{
|
||||||
$this->application = $application;
|
$this->application = $application;
|
||||||
@ -1083,18 +1094,17 @@ class SimpleXLSXGen
|
|||||||
$this->sheets[$this->curSheet]['autofilter'] = $range;
|
$this->sheets[$this->curSheet]['autofilter'] = $range;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mergeCells($range)
|
public function mergeCells($range)
|
||||||
{
|
{
|
||||||
$this->sheets[$this->curSheet]['mergecells'][] = $range;
|
$this->sheets[$this->curSheet]['mergecells'][] = $range;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setColWidth($col, $width)
|
public function setColWidth($col, $width)
|
||||||
{
|
{
|
||||||
$this->sheets[$this->curSheet]['colwidth'][$col] = $width;
|
$this->sheets[$this->curSheet]['colwidth'][$col] = $width;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rightToLeft($value = true)
|
public function rightToLeft($value = true)
|
||||||
{
|
{
|
||||||
$this->rtl = $value;
|
$this->rtl = $value;
|
||||||
@ -1122,19 +1132,31 @@ class SimpleXLSXGen
|
|||||||
return $id;
|
return $id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static function raw($value)
|
public static function raw($value)
|
||||||
{
|
{
|
||||||
return "\0" . $value;
|
return "\0" . $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function cell2coord($cell, &$x, &$y)
|
public function freezePanes($cell)
|
||||||
|
{
|
||||||
|
$this->sheets[$this->curSheet]['frozen'] = $cell;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert A1 cell reference style to R1C1 cell reference style (row/col number starting from 1)
|
||||||
|
*
|
||||||
|
* @param string $cell Cell reference in A1 format
|
||||||
|
*
|
||||||
|
* @return array Cell reference in R1C1 format as a two element array [row (ie. y coord), col (ie. x coord)]
|
||||||
|
*/
|
||||||
|
public static function cell2coord($cell)
|
||||||
{
|
{
|
||||||
$x = $y = 0;
|
$x = $y = 0;
|
||||||
$lettercount = 0;
|
$lettercount = 0;
|
||||||
$cell = str_replace([' ', '\t', '\r', '\n', '\v', '\0'], '', $cell);
|
$cell = str_replace([' ', '\t', '\r', '\n', '\v', '\0', '$'], '', $cell);
|
||||||
if (empty($cell)) {
|
if (empty($cell)) {
|
||||||
return;
|
return [];
|
||||||
}
|
}
|
||||||
$cell = strtoupper($cell);
|
$cell = strtoupper($cell);
|
||||||
for ($i = 0, $len = strlen($cell); $i < $len; $i++) {
|
for ($i = 0, $len = strlen($cell); $i < $len; $i++) {
|
||||||
@ -1149,24 +1171,196 @@ class SimpleXLSXGen
|
|||||||
$x += (ord($cell[$i]) - ord('A') + 1) * (26 ** $e);
|
$x += (ord($cell[$i]) - ord('A') + 1) * (26 ** $e);
|
||||||
$e++;
|
$e++;
|
||||||
}
|
}
|
||||||
|
$x++; //to make 1-based
|
||||||
}
|
}
|
||||||
if ($lettercount < strlen($cell)) {
|
if ($lettercount < strlen($cell)) {
|
||||||
$y = ((int)substr($cell, $lettercount)) - 1;
|
$y = ((int)substr($cell, $lettercount));
|
||||||
}
|
}
|
||||||
|
return [$y, $x];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function coord2cell($x, $y)
|
/**
|
||||||
|
* Convert R1C1 cell reference style (row/col 1-based) to A1 cell reference style
|
||||||
|
*
|
||||||
|
* @param integer $y Row number (starting from 1) or a 2 element array with 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 = '';
|
$c = '';
|
||||||
for ($i = $x; $i >= 0; $i = ((int)($i / 26)) - 1) {
|
for ($i = $x - 1; $i >= 0; $i = ((int)($i / 26)) - 1) {
|
||||||
$c = chr(ord('A') + $i % 26) . $c;
|
$c = chr(ord('A') + $i % 26) . $c;
|
||||||
}
|
}
|
||||||
return $c . ($y + 1);
|
return $c . strval($y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function freezePanes($cell)
|
/**
|
||||||
|
* Convert A1 range reference style to R1C1 cell reference style
|
||||||
|
*
|
||||||
|
* @param string $range Range reference in A1 format
|
||||||
|
*
|
||||||
|
* @return array Cell reference in R1C1 format as a four element array [top-left row, top-left col, bottom-right row, bottom-right col]
|
||||||
|
*/
|
||||||
|
public static function range2coord($range)
|
||||||
{
|
{
|
||||||
$this->sheets[$this->curSheet]['frozen'] = $cell;
|
$temp = explode(':', $range);
|
||||||
return $this;
|
if (empty($temp[1])) return self::cell2coord($temp[0]);
|
||||||
|
return array_merge(self::cell2coord($temp[0]), self::cell2coord($temp[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert R1C1 range reference style (row/col 1-based) to A1 range reference style
|
||||||
|
*
|
||||||
|
* 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 element array)
|
||||||
|
* @param array $style Associative array with style attributes name/value pairs to change/add
|
||||||
|
* @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 oring) 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) && $edge !== (self::A_TOP + self::A_RIGHT + self::A_BOTTOM + self::A_LEFT)) {
|
||||||
|
//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 following $edge
|
||||||
|
if ($edge & self::A_TOP) $newBorder[0] = $border[0];
|
||||||
|
if ($edge & self::A_RIGHT) $newBorder[1] = $border[1];
|
||||||
|
if ($edge & self::A_BOTTOM) $newBorder[2] = $border[2];
|
||||||
|
if ($edge & self::A_LEFT) $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 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 void
|
||||||
|
*/
|
||||||
|
public static function setTagAttributes($str, $tag, $attributes)
|
||||||
|
{
|
||||||
|
$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