Comments support

This commit is contained in:
Javier 2023-07-27 14:12:35 -03:00
parent 8b1cdddee3
commit 1d5713d681
2 changed files with 184 additions and 37 deletions

View File

@ -1,4 +1,5 @@
# SimpleXLSXGen
[<img src="https://img.shields.io/github/license/shuchkin/simplexlsxgen" />](https://github.com/shuchkin/simplexlsxgen/blob/master/license.md) [<img src="https://img.shields.io/github/stars/shuchkin/simplexlsxgen" />](https://github.com/shuchkin/simplexlsxgen/stargazers) [<img src="https://img.shields.io/github/forks/shuchkin/simplexlsxgen" />](https://github.com/shuchkin/simplexlsxgen/network) [<img src="https://img.shields.io/github/issues/shuchkin/simplexlsxgen" />](https://github.com/shuchkin/simplexlsxgen/issues)
Export data to Excel XLSX file. PHP XLSX generator. No external tools and libraries.
@ -11,6 +12,7 @@ Export data to Excel XLSX file. PHP XLSX generator. No external tools and librar
*Hey, bro, please ★ the package for my motivation :) and [donate](https://opencollective.com/simplexlsx) for more motivation!*
## Basic Usage
```php
$books = [
['ISBN', 'title', 'author', 'publisher', 'ctry' ],
@ -23,6 +25,7 @@ $xlsx->saveAs('books.xlsx'); // or downloadAs('books.xlsx') or $xlsx_content = (
![XLSX screenshot](books.png)
## Installation
The recommended way to install this library is [through Composer](https://getcomposer.org).
[New to Composer?](https://getcomposer.org/doc/00-intro.md)
@ -33,6 +36,7 @@ $ composer require shuchkin/simplexlsxgen
or download class [here](https://github.com/shuchkin/simplexlsxgen/blob/master/src/SimpleXLSXGen.php)
## Examples
Use UTF-8 encoded strings.
### Data types
@ -136,6 +140,9 @@ exit();
// Autofilter
$xlsx->autoFilter('A1:B10');
// Comment cell
$xlsx->setComment("B2", "Comment text", "Comment author");
// Freeze rows and columns from top-left corner up to, but not including,
// the row and column of the indicated cell (in A1 format as string or
// R1C1 format as 2-element integer array)
@ -158,6 +165,7 @@ $xlsx->setAuthor('Sergey Shuchkin <sergey.shuchkin@gmail.com>')
->setCategory('This is Сategory')
->setApplication('Shuchkin\SimpleXLSXGen')
```
### JS array to Excel (AJAX)
```php
<?php // array2excel.php
@ -224,8 +232,11 @@ function array2excel() {
* When XLSX file is generated (__toString, saveAs or downloadAs methods), the data matrix of each sheet is traversed by rows and columns using foreach cycles. This implies freedom in the type of indexes used for the data but it also implies paying special attention when using numeric indexes since the array is traversed in the order in which they were defined and not in the numerical order of said indexes.
* The helper methods for setting and getting the styles of a range or cell, implies that the data was defined as a 2-dimensional array (matrix) with consecutive 0-based numeric indexes.
* If you use download or downloadAs methods, make sure you don't generate output before (not important if you use output buffering) and after you call the method. Pay special attention to whitespaces characters (space, cr/lf, tab) in source files before and after PHP code closing tags. If you inadvertently, or on purpose, generate text output, the resulting XLSX file will be corrupted.
* Methods setDefaultFont, setDefaultFontSize, setTitle, setSubject, setAuthor, setCompany, setManager, setKeywords, setDescription, setCategory, setApplication, setLastModifiedBy and rightToLeft apply to the entire book.
* Methods autoFilter, mergeCells, setColWidth, freezePanes and setComment apply to the current sheet (the last addSheet/fromArray used)
## Debug
```php
ini_set('error_reporting', E_ALL );
ini_set('display_errors', 1 );

View File

@ -110,9 +110,9 @@ class SimpleXLSXGen
$this->defaultFont = 'Calibri';
$this->defaultFontSize = 10;
$this->rtl = false;
$this->sheets = [['name' => 'Sheet1', 'rows' => [], 'hyperlinks' => [], 'mergecells' => [], 'colwidth' => [], 'autofilter' => '']];
$this->sheets = [];
$this->extLinkId = 0;
$this->SI = []; // sharedStrings index
$this->SI = []; // sharedStrings index
$this->SI_KEYS = []; // & keys
// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_numFmts_topic_ID0E6KK6.html
@ -179,18 +179,27 @@ class SimpleXLSXGen
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' . self::NEWLINE .
'{RELS}' . self::NEWLINE .
'</Relationships>',
'xl/drawings/vmlDrawing1.vml' => '<xml xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel">' . self::NEWLINE .
'<o:shapelayout v:ext="edit"><o:idmap v:ext="edit" data="1"/></o:shapelayout>' . self::NEWLINE .
'<v:shapetype id="_x0000_t202" coordsize="21600,21600" o:spt="202" path="m,l,21600r21600,l21600,xe"><v:stroke joinstyle="miter"/><v:path gradientshapeok="t" o:connecttype="rect"/></v:shapetype>' . self::NEWLINE .
'{SHAPES}</xml>',
'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">' . self::NEWLINE .
'<dimension ref="{REF}"/>' . self::NEWLINE .
'{SHEETVIEWS}' . self::NEWLINE .
'{COLS}' . self::NEWLINE .
'<sheetData>' . self::NEWLINE . '{ROWS}' . self::NEWLINE . '</sheetData>' . self::NEWLINE .
'{AUTOFILTER}' . '{MERGECELLS}' . self::NEWLINE . '{HYPERLINKS}' . self::NEWLINE .
'{AUTOFILTER}' . '{MERGECELLS}' . self::NEWLINE . '{HYPERLINKS}' . self::NEWLINE . '{COMMENTS}' .
'</worksheet>',
'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">' . self::NEWLINE .
'{HYPERLINKS}' . self::NEWLINE .
'</Relationships>',
'xl/comments1.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . self::NEWLINE .
'<comments xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">' . self::NEWLINE .
'{AUTHORS}' . self::NEWLINE .
'{COMMENTS}' . self::NEWLINE .
'</comments>',
'xl/sharedStrings.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . self::NEWLINE .
'<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="{CNT}" uniqueCount="{CNT}">' . self::NEWLINE .
'{STRINGS}' . self::NEWLINE .
@ -258,11 +267,23 @@ class SimpleXLSXGen
}
}
}
$this->sheets[$this->curSheet] = ['name' => $name, 'hyperlinks' => [], 'mergecells' => [], 'colwidth' => [], 'autofilter' => '', 'frozen' => ''];
if (isset($rows[0]) && is_array($rows[0])) {
$this->sheets[$this->curSheet]['rows'] = $rows;
} else {
$this->sheets[$this->curSheet]['rows'] = [];
$this->sheets[$this->curSheet] = [
'name' => $name,
'rows' => [],
'hyperlinks' => [],
'mergecells' => [],
'colwidth' => [],
'autofilter' => '',
'frozen' => '',
'authors' => [],
'comments' => [],
'commentDrawingId' => null
];
if (is_array($rows)) {
foreach ($rows as $row) {
if (is_array($row)) $this->sheets[$this->curSheet]['rows'] = $rows;
break;
}
}
return $this;
}
@ -340,22 +361,22 @@ class SimpleXLSXGen
if ($cfilename === 'xl/_rels/workbook.xml.rels') {
$s = '';
for ($i = 0; $i < $cnt_sheets; $i++) {
$s .= '<Relationship Id="rId' . ($i + 1) . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"' .
' Target="worksheets/sheet' . ($i + 1) . '.xml"/>' . self::NEWLINE;
$s .= '<Relationship Id="rId' . ($i + 1) . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" ' .
'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"/>' . self::NEWLINE;
$s .= '<Relationship Id="rId' . ($i + 2) . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>';
$s .= '<Relationship Id="rId' . ($cnt_sheets + 1) . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>' . self::NEWLINE;
$s .= '<Relationship Id="rId' . ($cnt_sheets + 2) . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>';
$template = str_replace('{RELS}', $s, $template);
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
$entries++;
} elseif ($cfilename === 'xl/workbook.xml') {
$s = '';
foreach ($this->sheets as $k => $v) {
$s .= '<sheet name="' . $this->esc($v['name']) . '" sheetId="' . ($k + 1) . '" r:id="rId' . ($k + 1) . '"/>';
$s .= '<sheet name="' . self::esc($v['name']) . '" sheetId="' . ($k + 1) . '" r:id="rId' . ($k + 1) . '"/>';
}
$template = str_replace(
['{SHEETS}', '{APP}'],
[$s, $this->esc($this->application)],
[$s, self::esc($this->application)],
$template
);
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
@ -363,7 +384,7 @@ class SimpleXLSXGen
} elseif ($cfilename === 'docProps/app.xml') {
$template = str_replace(
['{APP}', '{COMPANY}', '{MANAGER}'],
[$this->esc($this->application), $this->esc($this->company), $this->esc($this->manager)],
[self::esc($this->application), self::esc($this->company), self::esc($this->manager)],
$template
);
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
@ -371,7 +392,7 @@ class SimpleXLSXGen
} elseif ($cfilename === 'docProps/core.xml') {
$template = str_replace(
['{DATE}', '{AUTHOR}', '{TITLE}', '{SUBJECT}', '{KEYWORD}', '{DESCRIPTION}', '{CATEGORY}', '{LAST_MODIFY_BY}'],
[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)],
[gmdate('Y-m-d\TH:i:s\Z'), self::esc($this->author), self::esc($this->title), self::esc($this->subject), self::esc($this->keywords), self::esc($this->description), self::esc($this->category), self::esc($this->lastModifiedBy)],
$template
);
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
@ -395,6 +416,70 @@ class SimpleXLSXGen
$entries++;
}
$xml = null;
} elseif ($cfilename === 'xl/comments1.xml') {
foreach ($this->sheets as $k => $v) {
$cComments = count($v['comments']);
$cAuthors = count($v['authors']);
if ($cComments) {
$filename = 'xl/comments' . ($k + 1) . '.xml';
//create author(s) list and set author Id
$authorId = -1; //index for document author to use in comments
$AUTHORS = '<authors>' . self::NEWLINE;
for ($i = 0; $i < $cAuthors; $i++) {
$AUTHORS .= ' <author>' . $v['authors'][$i] . '</author>' . self::NEWLINE;
if ($v['authors'][$i] === __CLASS__) $authorId = $i;
}
if ($authorId === -1) {
$authorId = $cAuthors;
$AUTHORS .= ' <author>' . __CLASS__ . '</author>' . self::NEWLINE;
}
$AUTHORS .= '</authors>';
//comments list
$COMMENTS = '<commentList>' . self::NEWLINE;
for ($i = 0; $i < $cComments; $i++) {
$COMMENTS .= '<comment ref="' . $v['comments'][$i]['cell'] . '" authorId="' . (($v['comments'][$i]['authorId'] !== -1) ? $v['comments'][$i]['authorId'] : $authorId) . '" shapeId="0"><text>' . self::NEWLINE;
//prefix with author name if exist
if ($v['comments'][$i]['authorId'] !== -1) {
$COMMENTS .= ' <r><rPr><b></b><sz val="9"/><rFont val="Tahoma"/><charset val="1"/></rPr><t xml:space="preserve">' . $v['authors'][$v['comments'][$i]['authorId']] . ':' . self::NEWLINE . '</t></r>';
}
$preserve = (strpbrk($v['comments'][$i]['comment'], "\t\r\n") !== false) ? ' xml:space="preserve"' : '';
$COMMENTS .= ' <r><rPr><sz val="9"/><rFont val="Tahoma"/><charset val="1"/></rPr><t' . $preserve . '>' . $v['comments'][$i]['comment'] . '</t></r>' . self::NEWLINE;
$COMMENTS .= '</text></comment>' . self::NEWLINE;
}
$COMMENTS .= '</commentList>';
$xml = str_replace(['{AUTHORS}', '{COMMENTS}'], [$AUTHORS, $COMMENTS], $template);
$this->_writeEntry($fh, $cdrec, $filename, $xml);
$entries++;
}
}
$xml = null;
} elseif ($cfilename === 'xl/drawings/vmlDrawing1.vml') {
foreach ($this->sheets as $k => $v) {
$cComments = count($v['comments']);
if ($cComments) {
$SHAPES = '';
$filename = 'xl/drawings/vmlDrawing' . ($k + 1) . '.vml';
for ($i = 0; $i < $cComments; $i++) {
[$row, $col] = self::cell2coord($v['comments'][$i]['cell']);
$SHAPES .= '<v:shape id="_x0000_s' . $v['comments'][$i]['cell'] . '" type="#_x0000_t202" style="z-index:2;visibility:hidden" fillcolor="#ffffe1">' . self::NEWLINE .
' <v:fill color2="#ffffe1"/>' . self::NEWLINE .
' <v:path o:connecttype="none"/>' . self::NEWLINE .
' <x:ClientData ObjectType="Note">' . self::NEWLINE .
' <x:MoveWithCells/>' . self::NEWLINE .
' <x:SizeWithCells/>' . self::NEWLINE .
' <x:Anchor>'.$col.', 0, '.($row-2).', 0, '.($col+2).', 0, '.($row+1).', 0</x:Anchor>' . self::NEWLINE .
' <x:AutoFill>False</x:AutoFill>' . self::NEWLINE .
' <x:Row>'.($row-1).'</x:Row>' . self::NEWLINE .
' <x:Column>'.($col-1).'</x:Column>' . self::NEWLINE .
' </x:ClientData>' . self::NEWLINE .
'</v:shape>' . self::NEWLINE;
}
$xml = str_replace('{SHAPES}', $SHAPES, $template);
$this->_writeEntry($fh, $cdrec, $filename, $xml);
$entries++;
}
}
$xml = null;
} elseif ($cfilename === 'xl/worksheets/_rels/sheet1.xml.rels') {
foreach ($this->sheets as $k => $v) {
if ($this->extLinkId) {
@ -402,9 +487,13 @@ class SimpleXLSXGen
$filename = 'xl/worksheets/_rels/sheet' . ($k + 1) . '.xml.rels';
foreach ($v['hyperlinks'] as $h) {
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="' . self::esc($h['H']) . '" TargetMode="External"/>';
}
}
if ($v['commentDrawingId']) {
$RH[] = ' <Relationship Id="rId' . $v['commentDrawingId'] . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing" Target="../drawings/vmlDrawing' . ($k + 1) . '.vml"/>';
$RH[] = ' <Relationship Id="rId' . ($v['commentDrawingId'] + 1) . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments" Target="../comments' . ($k + 1) . '.xml"/>';
}
$xml = str_replace('{HYPERLINKS}', implode(self::NEWLINE, $RH), $template);
$this->_writeEntry($fh, $cdrec, $filename, $xml);
$entries++;
@ -412,13 +501,20 @@ class SimpleXLSXGen
}
$xml = null;
} elseif ($cfilename === '[Content_Types].xml') {
$hasComments = false;
//if $TYPES is initialized always with this value, why not add it to the template?
$TYPES = ['<Override PartName="/_rels/.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>'];
foreach ($this->sheets as $k => $v) {
$TYPES[] = '<Override PartName="/xl/worksheets/sheet' . ($k + 1) . '.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>';
if ($this->extLinkId) {
$TYPES[] = '<Override PartName="/xl/worksheets/_rels/sheet' . ($k + 1) . '.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
}
if (count($v['comments'])) {
$hasComments = true;
$TYPES[] = '<Override PartName="/xl/comments' . ($k + 1) . '.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml"/>';
}
}
if ($hasComments) $TYPES[] = '<Default Extension="vml" ContentType="application/vnd.openxmlformats-officedocument.vmlDrawing"/>';
$template = str_replace('{TYPES}', implode(self::NEWLINE, $TYPES), $template);
$this->_writeEntry($fh, $cdrec, $cfilename, $template);
$entries++;
@ -499,7 +595,7 @@ class SimpleXLSXGen
$ba[] = $ba[0];
}
if (!isset($ba[4])) { // diagonal
$ba[] = 'none';
$ba[] = 'none';
}
$sides = ['left' => 3, 'right' => 1, 'top' => 0, 'bottom' => 2, 'diagonal' => 4];
foreach ($sides as $side => $idx) {
@ -710,7 +806,7 @@ class SimpleXLSXGen
}
$cname = $this->num2name($CUR_COL) . $CUR_ROW;
if ($v === null || $v === '') {
$row .= ' <c r="' . $cname . '"/>';
$row .= ' <c r="' . $cname . '"/>';
continue;
}
$ct = $cv = $cf = null;
@ -873,7 +969,7 @@ class SimpleXLSXGen
}
}
if ($cv === null) {
$v = $this->esc($v);
$v = self::esc($v);
if ($cf) {
$ct = 'str';
$cv = $v;
@ -963,16 +1059,23 @@ class SimpleXLSXGen
if (count($this->sheets[$idx]['hyperlinks'])) {
$HYPERLINKS[] = '<hyperlinks>';
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="' . self::esc($h['L']) . '" display="' . self::esc($h['H'] . ($h['L'] ? ' - ' . $h['L'] : '')) . '"/>';
}
$HYPERLINKS[] = '</hyperlinks>';
}
$COMMENTS = '';
if (count($this->sheets[$idx]['comments'])) {
$this->extLinkId++;
$this->sheets[$idx]['commentDrawingId'] = $this->extLinkId;
$COMMENTS = '<legacyDrawing r:id="rId' . $this->extLinkId . '"/>' . self::NEWLINE;
}
//restore locale
setlocale(LC_NUMERIC, $_loc);
return str_replace(
['{REF}', '{COLS}', '{ROWS}', '{AUTOFILTER}', '{MERGECELLS}', '{HYPERLINKS}', '{SHEETVIEWS}'],
['{REF}', '{COLS}', '{ROWS}', '{AUTOFILTER}', '{MERGECELLS}', '{HYPERLINKS}', '{SHEETVIEWS}', '{COMMENTS}'],
[
$REF,
implode(self::NEWLINE, $COLS),
@ -980,7 +1083,8 @@ class SimpleXLSXGen
$AUTOFILTER,
implode(self::NEWLINE, $MERGECELLS),
implode(self::NEWLINE, $HYPERLINKS),
$SHEETVIEWS
$SHEETVIEWS,
$COMMENTS
],
$template
);
@ -1089,6 +1193,11 @@ class SimpleXLSXGen
$this->lastModifiedBy = $lastModifiedBy;
return $this;
}
public function rightToLeft($value = true)
{
$this->rtl = $value;
return $this;
}
public function autoFilter($range)
{
@ -1105,18 +1214,44 @@ class SimpleXLSXGen
$this->sheets[$this->curSheet]['colwidth'][$col] = $width;
return $this;
}
public function rightToLeft($value = true)
public function freezePanes($cell)
{
$this->rtl = $value;
$this->sheets[$this->curSheet]['frozen'] = $cell;
return $this;
}
public function esc($str)
/**
* Set comment of a cell of the current sheet
*
* @param string|array $cell Cell reference in A1 format (string) or R1C1 format (2-element array)
* @param string $comment Text comment
* @param string $author Optional. If specified, the comment will have $author as a preffix (in bold) of comment
*
* @return void
*/
public function setComment($cell, $comment, $author = '')
{
// XML UTF-8: #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
// but we use fast version
return str_replace(['&', '<', '>', "\x00", "\x03", "\x0B"], ['&amp;', '&lt;', '&gt;', '', '', ''], $str);
if (is_array($cell)) {
$cell = self::coord2cell($cell);
} else {
$cell = trim(strtoupper($cell));
}
//search author index
$index = -1;
if ($author !== '') {
$cAuthors = count($this->sheets[$this->curSheet]['authors']);
for ($i = 0; $i < $cAuthors; $i++) {
if ($author === $this->sheets[$this->curSheet]['authors'][$i]) {
$index = $i;
break;
}
}
if ($index === -1) {
$this->sheets[$this->curSheet]['authors'][] = $author;
$index = $cAuthors;
}
}
$this->sheets[$this->curSheet]['comments'][] = ['cell' => $cell, 'comment' => $comment, 'authorId' => $index];
return $this;
}
public function getNumFmtId($code)
@ -1133,17 +1268,18 @@ class SimpleXLSXGen
return $id;
}
public static function esc($str)
{
// XML UTF-8: #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
// but we use fast version
return str_replace(['&', '<', '>', "\x00", "\x03", "\x0B"], ['&amp;', '&lt;', '&gt;', '', '', ''], $str);
}
public static function raw($value)
{
return "\0" . $value;
}
public function freezePanes($cell)
{
$this->sheets[$this->curSheet]['frozen'] = $cell;
return $this;
}
/**
* Convert A1 cell reference format to R1C1 cell reference format (row/col number starting from 1)
*