Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
odan committed Nov 8, 2023
1 parent 75e057a commit 94baeb2
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 23 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,22 @@ Extreme fast in-memory Excel (XLSX) file writer.
- Compatibility with LibreOffice / OpenOffice Calc.
- In-memory operation by default.
- Optional hard disk access, when memory limitations are reached.
- Multiple sheets in a workbook.
- Header columns with bold font.
- Custom worksheet name.

## Limitations

The purpose of this package is to provide a very fast and
memory efficient XLSX file generator. It's designed for
* Number of workbooks: Limited by available memory and system resources.
* Sheets in a workbook: Limited by available memory (default is 1 sheet).
* Maximal number of columns: 16,384 (specification limit)
* Maximal number of rows: 1,048,576 (specification limit)
* Font styles: 2 (normal for rows and **bold** for columns)

The purpose of this package is to provide a very fast and
memory efficient Excel (XLSX) file generator. It is designed for
very fast data output, but not for fancy worksheet styles.
If you need more flexibility in terms of multiple
sheets and colorful designs, you may use a
If you need more layout and color options, you may better use a
different package, such as PhpSpreadsheet.

## Installation
Expand Down
20 changes: 13 additions & 7 deletions src/ExcelWorkbook.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,14 @@ private function createWorkbookXml(): string
$sheets = $dom->createElement('sheets');
$workbook->appendChild($sheets);

$sheetId = 1;
$relationshipId = 3;
foreach ($this->sheets as $excelSheet) {
$sheet = $dom->createElement('sheet');
$sheets->appendChild($sheet);
$sheet->setAttribute('name', $excelSheet->getName());
$sheet->setAttribute('sheetId', '1'); // @todo Dynamic?
$sheet->setAttribute('r:id', 'rId2'); // @todo Dynamic?
$sheet->setAttribute('sheetId', (string)$sheetId++);
$sheet->setAttribute('r:id', sprintf('rId%s', $relationshipId++));
}

$calcPr = $dom->createElement('calcPr');
Expand Down Expand Up @@ -368,16 +370,20 @@ private function createWorkbookRelsXml(): string
],
[
'Id' => 'rId2',
'Type' => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet',
'Target' => 'worksheets/sheet1.xml',
],
[
'Id' => 'rId3',
'Type' => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings',
'Target' => 'sharedStrings.xml',
],
];

$relationshipId = 3;
foreach ($this->sheets as $index => $sheet) {
$relationshipData[] = [
'Id' => sprintf('rId%s', $relationshipId++),
'Type' => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet',
'Target' => sprintf('worksheets/sheet%s.xml', $index + 1),
];
}

$this->createElements($dom, $relationships, 'Relationship', $relationshipData);

return (string)$dom->saveXML();
Expand Down
35 changes: 23 additions & 12 deletions src/ExcelWorksheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Odan\Excel;

use DOMDocument;
use DOMElement;
use DOMNode;

final class ExcelWorksheet
Expand All @@ -17,6 +18,10 @@ final class ExcelWorksheet

private SharedStrings $sharedStrings;

private DOMElement $dimension;
private int $boundRow = 1;
private int $boundColumn = 1;

public function __construct(SharedStrings $sharedStrings)
{
$this->sharedStrings = $sharedStrings;
Expand All @@ -34,12 +39,9 @@ public function addColumns(array $values): void
$this->sheetData->appendChild($row);
$row->setAttribute('r', (string)++$this->rowIndex);

// @todo dynamic spans
$row->setAttribute('spans', '1:3');

foreach ($values as $colIndex => $value) {
$column = $this->sheetXml->createElement('c');
$column->setAttribute('r', $this->mapRowColumnToString($this->rowIndex, $colIndex + 1));
$column->setAttribute('r', $this->getCoordinate($this->rowIndex, $colIndex + 1));
$row->appendChild($column);

// Apply the cell style by referencing it through the s attribute
Expand All @@ -57,12 +59,10 @@ public function addRow(array $values): void
$row = $this->sheetXml->createElement('row');
$this->sheetData->appendChild($row);
$row->setAttribute('r', (string)++$this->rowIndex);
// @todo dynamic spans
$row->setAttribute('spans', '1:3');

foreach ($values as $colIndex => $value) {
$column = $this->sheetXml->createElement('c');
$column->setAttribute('r', $this->mapRowColumnToString($this->rowIndex, $colIndex + 1));
$column->setAttribute('r', $this->getCoordinate($this->rowIndex, $colIndex + 1));
$row->appendChild($column);

// s = 0 = Normal font (see styles.xml)
Expand All @@ -74,10 +74,16 @@ public function addRow(array $values): void
}
}

private function mapRowColumnToString(int $row, int $column): string
private function getCoordinate(int $row, int $column): string
{
$columnLetter = '';

// Maximum limit
$column = min($column, 16384);

$this->boundRow = max($this->boundRow, $row);
$this->boundColumn = max($this->boundColumn, $column);

while ($column > 0) {
$remainder = ($column - 1) % 26;
$columnLetter = chr(65 + $remainder) . $columnLetter;
Expand All @@ -90,6 +96,10 @@ private function mapRowColumnToString(int $row, int $column): string

public function createSheetXml(): string
{
// The row and column bounds of all cells in this worksheet
$bound = $this->getCoordinate($this->boundRow, $this->boundColumn);
$this->dimension->setAttribute('ref', 'A1:' . $bound);

return (string)$this->sheetXml->saveXML();
}

Expand All @@ -113,10 +123,11 @@ private function initSheetXml(): void
$worksheet->setAttribute('mc:Ignorable', 'x14ac xr xr2 xr3');
$worksheet->setAttribute('xr:uid', '{00000000-0001-0000-0000-000000000000}');

$dimension = $this->sheetXml->createElement('dimension');
$worksheet->appendChild($dimension);
// @todo make dynamic
$dimension->setAttribute('ref', 'A1:C4');
$this->dimension = $this->sheetXml->createElement('dimension');
$worksheet->appendChild($this->dimension);

// The row and column bounds of all cells in this worksheet
$this->dimension->setAttribute('ref', 'A1:A1');

$sheetViews = $this->sheetXml->createElement('sheetViews');
$worksheet->appendChild($sheetViews);
Expand Down
17 changes: 17 additions & 0 deletions tests/ExcelWriterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ public function test(): void
$sheet->addRow($rowData);
}

$sheet2 = $workbook->addSheet('My Sheet 2');

// Header columns
$columns = ['Date', 'Name', 'Amount', 'Fee'];
$sheet2->addColumns($columns);

$data = [
['2023-12-31', 'Max', '220', '0.1'],
['2023-8-23', 'John', '1234.5', '0.3'],
['2023-06-01', 'Daniel', '6789.12', '1.4'],
];

// Write data
foreach ($data as $rowData) {
$sheet2->addRow($rowData);
}

$file = new ExcelFile();
$workbook->save($file);

Expand Down

0 comments on commit 94baeb2

Please sign in to comment.