diff --git a/src/ext/spreadsheet/PhpSpreadsheetBuilder.php b/src/ext/spreadsheet/PhpSpreadsheetBuilder.php new file mode 100644 index 0000000..41465f9 --- /dev/null +++ b/src/ext/spreadsheet/PhpSpreadsheetBuilder.php @@ -0,0 +1,114 @@ +ss = new Spreadsheet(); + $this->valueBinder = new StringValueBinder(); + $this->setWsname($params["wsname"] ?? static::WSNAME); + } + + protected Spreadsheet $ss; + + protected IValueBinder $valueBinder; + + protected ?Worksheet $ws; + + protected int $nrow; + + const STYLE_ROW = 0, STYLE_HEADER = 1; + + protected int $rowStyle; + + /** + * @param string|int|null $wsname + */ + function setWsname($wsname): self { + $ss = $this->ss; + $this->ws = null; + $this->nrow = 0; + $this->rowStyle = self::STYLE_ROW; + + $ws = wsutils::get_ws($wsname, $ss); + if ($ws === null) { + $ws = $ss->createSheet()->setTitle($wsname); + $this->wroteHeaders = false; + } else { + $maxRow = wsutils::compute_max_coords($ws)[1]; + $this->nrow = $maxRow - 1; + $this->wroteHeaders = $maxRow > 1; + } + $this->ws = $ws; + return $this; + } + + function _write(array $row): void { + $ws = $this->ws; + $styleHeader = $this->rowStyle === self::STYLE_HEADER; + $nrow = ++$this->nrow; + $ncol = 1; + foreach ($row as $col) { + $ws->getCellByColumnAndRow($ncol++, $nrow)->setValue($col, $this->valueBinder); + } + if ($styleHeader) { + $ws->getStyle("$nrow:$nrow")->getFont()->setBold(true); + $maxcol = count($row); + for ($ncol = 1; $ncol <= $maxcol; $ncol++) { + $ws->getColumnDimensionByColumn($ncol)->setAutoSize(true); + } + } + } + + function writeHeaders(?array $headers=null): void { + $this->rowStyle = self::STYLE_HEADER; + parent::writeHeaders($headers); + $this->rowStyle = self::STYLE_ROW; + } + + function _sendContentType(): void { + switch (path::ext($this->output)) { + case ".ods": + $contentType = "application/vnd.oasis.opendocument.spreadsheet"; + break; + case ".xlsx": + default: + $contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + break; + } + http::content_type($contentType); + } + + function _sendFile(): int { + switch (path::ext($this->output)) { + case ".ods": + $writer = new Ods($this->ss); + break; + case ".xlsx": + default: + $writer = new Xlsx($this->ss); + break; + } + $this->rewind(); + $writer->save($this->getResource()); + $this->rewind(); + $this->sendHeaders(); + return $this->fpassthru(); + } +} diff --git a/src/ext/spreadsheet/PhpSpreadsheetReader.php b/src/ext/spreadsheet/PhpSpreadsheetReader.php new file mode 100644 index 0000000..062145f --- /dev/null +++ b/src/ext/spreadsheet/PhpSpreadsheetReader.php @@ -0,0 +1,117 @@ + self::DATETIME_FORMAT, + 'dd/mm hh' => self::DATETIME_FORMAT, + 'mm/dd hh:mm' => self::DATETIME_FORMAT, + 'dd/mm hh:mm' => self::DATETIME_FORMAT, + 'mm/dd hh:mm:ss' => self::DATETIME_FORMAT, + 'dd/mm hh:mm:ss' => self::DATETIME_FORMAT, + 'mm/dd/yyyy hh' => self::DATETIME_FORMAT, + 'dd/mm/yyyy hh' => self::DATETIME_FORMAT, + 'mm/dd/yyyy hh:mm' => self::DATETIME_FORMAT, + 'dd/mm/yyyy hh:mm' => self::DATETIME_FORMAT, + 'mm/dd/yyyy hh:mm:ss' => self::DATETIME_FORMAT, + 'dd/mm/yyyy hh:mm:ss' => self::DATETIME_FORMAT, + 'yyyy/mm/dd hh' => self::DATETIME_FORMAT, + 'yyyy/mm/dd hh:mm' => self::DATETIME_FORMAT, + 'yyyy/mm/dd hh:mm:ss' => self::DATETIME_FORMAT, + + 'mm/dd' => self::DATE_FORMAT, + 'dd/mm' => self::DATE_FORMAT, + 'mm/dd/yyyy' => self::DATE_FORMAT, + 'dd/mm/yyyy' => self::DATE_FORMAT, + 'yyyy/mm/dd' => self::DATE_FORMAT, + 'mm/yyyy' => self::DATE_FORMAT, + + 'hh AM/PM' => self::TIME_FORMAT, + 'hh:mm AM/PM' => self::TIME_FORMAT, + 'hh:mm:ss AM/PM' => self::TIME_FORMAT, + 'hh' => self::TIME_FORMAT, + 'hh:mm' => self::TIME_FORMAT, + 'hh:mm:ss' => self::TIME_FORMAT, + '[hh]:mm:ss' => self::TIME_FORMAT, + 'mm:ss' => self::TIME_FORMAT, + ]; + + /** @var string|int|null nom de la feuille depuis laquelle lire */ + const WSNAME = null; + + function __construct($input, ?array $params=null) { + parent::__construct($input, $params); + $this->wsname = $params["wsname"] ?? static::WSNAME; + } + + protected $wsname; + + /** + * @param string|int|null $wsname + */ + function setWsname($wsname): self { + $this->wsname = $wsname; + return $this; + } + + function getIterator() { + $ss = IOFactory::load($this->input); + $ws = wsutils::get_ws($this->wsname, $ss); + + [$nbCols, $nbRows] = wsutils::compute_max_coords($ws); + $this->isrc = $this->idest = 0; + for ($nrow = 1; $nrow <= $nbRows; $nrow++) { + $row = []; + for ($ncol = 1; $ncol <= $nbCols; $ncol++) { + if ($ws->cellExistsByColumnAndRow($ncol, $nrow)) { + $cell = $ws->getCellByColumnAndRow($ncol, $nrow); + $col = $cell->getValue(); + if ($col instanceof RichText) { + $col = $col->getPlainText(); + } else { + $dataType = $cell->getDataType(); + if ($dataType == DataType::TYPE_NUMERIC || $dataType == DataType::TYPE_FORMULA) { + # si c'est un format date, le forcer à une valeur standard + $origFormatCode = $cell->getStyle()->getNumberFormat()->getFormatCode(); + if (strpbrk($origFormatCode, "ymdhs") !== false) { + $formatCode = $origFormatCode; + $formatCode = preg_replace('/y+/', "yyyy", $formatCode); + $formatCode = preg_replace('/m+/', "mm", $formatCode); + $formatCode = preg_replace('/d+/', "dd", $formatCode); + $formatCode = preg_replace('/h+/', "hh", $formatCode); + $formatCode = preg_replace('/s+/', "ss", $formatCode); + $formatCode = preg_replace('/-+/', "/", $formatCode); + $formatCode = preg_replace('/\\\\ /', " ", $formatCode); + $formatCode = preg_replace('/;@$/', "", $formatCode); + $formatCode = cl::get(self::FORMAT_MAPPINGS, $formatCode, $formatCode); + if ($formatCode !== $origFormatCode) { + $cell->getStyle()->getNumberFormat()->setFormatCode($formatCode); + } + } + } + $col = $cell->getFormattedValue(); + $this->verifixCol($col); + } + } else { + $col = null; + } + $row[] = $col; + } + if ($this->cook($row)) { + yield $row; + $this->idest++; + } + $this->isrc++; + } + } +} diff --git a/src/ext/spreadsheet/SpoutBuilder.php b/src/ext/spreadsheet/SpoutBuilder.php new file mode 100644 index 0000000..d632353 --- /dev/null +++ b/src/ext/spreadsheet/SpoutBuilder.php @@ -0,0 +1,97 @@ +output)) { + case ".ods": + $ss = WriterEntityFactory::createODSWriter(); + break; + case ".xlsx": + default: + $ss = WriterEntityFactory::createXLSXWriter(); + break; + } + $ss->writeToStream($this->getResource()); + $this->ss = $ss; + $this->firstSheet = true; + $this->setWsname($params["wsname"] ?? static::WSNAME); + } + + protected WriterMultiSheetsAbstract $ss; + + const STYLE_ROW = 0, STYLE_HEADER = 1; + + protected int $rowStyle; + + protected bool $firstSheet; + + /** + * @param string|int|null $wsname + */ + function setWsname($wsname): self { + $ss = $this->ss; + $this->rowStyle = self::STYLE_ROW; + if ($this->firstSheet) { + $this->firstSheet = false; + $ws = $ss->getCurrentSheet(); + } else { + $ws = $ss->addNewSheetAndMakeItCurrent(); + } + if ($wsname !== null) $ws->setName($wsname); + $ws->setSheetView((new SheetView()) + ->setFreezeRow(2) + ); + return $this; + } + + function _write(array $row): void { + $row = WriterEntityFactory::createRowFromArray($row); + if ($this->rowStyle === self::STYLE_HEADER) { + $row->setStyle((new Style()) + ->setFontBold() + ); + } + $this->ss->addRow($row); + } + + function writeHeaders(?array $headers=null): void { + $this->rowStyle = self::STYLE_HEADER; + parent::writeHeaders($headers); + $this->rowStyle = self::STYLE_ROW; + } + + function _sendContentType(): void { + switch (path::ext($this->output)) { + case ".ods": + $contentType = "application/vnd.oasis.opendocument.spreadsheet"; + break; + case ".xlsx": + default: + $contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + break; + } + http::content_type($contentType); + } + + function _sendFile(): int { + $this->rewind(); + $this->sendHeaders(); + return $this->fpassthru(); + } +} diff --git a/src/ext/spreadsheet/SpoutReader.php b/src/ext/spreadsheet/SpoutReader.php new file mode 100644 index 0000000..c67fb0b --- /dev/null +++ b/src/ext/spreadsheet/SpoutReader.php @@ -0,0 +1,55 @@ +wsname = $params["wsname"] ?? static::WSNAME; + } + + protected $wsname; + + /** + * @param string|int|null $wsname + */ + function setWsname($wsname): self { + $this->wsname = $wsname; + return $this; + } + + function getIterator() { + $ss = ReaderEntityFactory::createReaderFromFile($this->input); + try { + $found = false; + foreach ($ss->getSheetIterator() as $ws) { + if ($this->wsname === null || $this->wsname === $ws->getName()) { + $found = true; + break; + } + } + if (!$found) return; + + $this->isrc = $this->idest = 0; + foreach ($ws->getRowIterator() as $row) { + $row = $row->toArray(); + foreach ($row as &$col) { + $this->verifixCol($col); + }; unset($col); + if ($this->cook($row)) { + yield $row; + $this->idest++; + } + $this->isrc++; + } + } finally { + $ss->close(); + } + } +} diff --git a/src/ext/spreadsheet/SsBuilder.php b/src/ext/spreadsheet/SsBuilder.php index 47cd7d7..6ddd9bd 100644 --- a/src/ext/spreadsheet/SsBuilder.php +++ b/src/ext/spreadsheet/SsBuilder.php @@ -1,118 +1,9 @@ ss = new Spreadsheet(); - $this->valueBinder = new StringValueBinder(); - $this->setWsname($params["wsname"] ?? static::WSNAME); - } - - protected Spreadsheet $ss; - - protected IValueBinder $valueBinder; - - protected ?Worksheet $ws; - - protected int $nrow; - - const STYLE_ROW = 0, STYLE_HEADER = 1; - - protected int $rowStyle; - - /** - * @param string|int|null $wsname - */ - function setWsname($wsname): self { - $ss = $this->ss; - $this->ws = null; - $this->nrow = 0; - $this->rowStyle = self::STYLE_ROW; - - $ws = wsutils::get_ws($wsname, $ss); - if ($ws === null) { - $ws = $ss->createSheet()->setTitle($wsname); - $this->wroteHeaders = false; - } else { - $maxRow = wsutils::compute_max_coords($ws)[1]; - $this->nrow = $maxRow - 1; - $this->wroteHeaders = $maxRow > 1; - } - $this->ws = $ws; - return $this; - } - - function _write(array $row): void { - $ws = $this->ws; - $styleHeader = $this->rowStyle === self::STYLE_HEADER; - $nrow = ++$this->nrow; - $ncol = 1; - foreach ($row as $col) { - $ws->getCellByColumnAndRow($ncol++, $nrow)->setValue($col, $this->valueBinder); - } - if ($styleHeader) { - $ws->getStyle("$nrow:$nrow")->getFont()->setBold(true); - $maxcol = count($row); - for ($ncol = 1; $ncol <= $maxcol; $ncol++) { - $ws->getColumnDimensionByColumn($ncol)->setAutoSize(true); - } - } - } - - function writeHeaders(?array $headers=null): void { - $this->rowStyle = self::STYLE_HEADER; - parent::writeHeaders($headers); - $this->rowStyle = self::STYLE_ROW; - } - - function _sendContentType(): void { - switch (path::ext($this->output)) { - case ".ods": - $contentType = "application/vnd.oasis.opendocument.spreadsheet"; - break; - case ".xlsx": - default: - $contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; - break; - } - http::content_type($contentType); - } - - function _sendFile(): int { - switch (path::ext($this->output)) { - case ".ods": - $writer = new Ods($this->ss); - break; - case ".xlsx": - default: - $writer = new Xlsx($this->ss); - break; - } - $this->rewind(); - $writer->save($this->getResource()); - $this->rewind(); - $this->sendHeaders(); - return $this->fpassthru(); - } +class SsBuilder extends SpoutBuilder { } diff --git a/src/ext/spreadsheet/SsReader.php b/src/ext/spreadsheet/SsReader.php index 5e4b1de..9523daf 100644 --- a/src/ext/spreadsheet/SsReader.php +++ b/src/ext/spreadsheet/SsReader.php @@ -1,119 +1,8 @@ self::DATETIME_FORMAT, - 'dd/mm hh' => self::DATETIME_FORMAT, - 'mm/dd hh:mm' => self::DATETIME_FORMAT, - 'dd/mm hh:mm' => self::DATETIME_FORMAT, - 'mm/dd hh:mm:ss' => self::DATETIME_FORMAT, - 'dd/mm hh:mm:ss' => self::DATETIME_FORMAT, - 'mm/dd/yyyy hh' => self::DATETIME_FORMAT, - 'dd/mm/yyyy hh' => self::DATETIME_FORMAT, - 'mm/dd/yyyy hh:mm' => self::DATETIME_FORMAT, - 'dd/mm/yyyy hh:mm' => self::DATETIME_FORMAT, - 'mm/dd/yyyy hh:mm:ss' => self::DATETIME_FORMAT, - 'dd/mm/yyyy hh:mm:ss' => self::DATETIME_FORMAT, - 'yyyy/mm/dd hh' => self::DATETIME_FORMAT, - 'yyyy/mm/dd hh:mm' => self::DATETIME_FORMAT, - 'yyyy/mm/dd hh:mm:ss' => self::DATETIME_FORMAT, - - 'mm/dd' => self::DATE_FORMAT, - 'dd/mm' => self::DATE_FORMAT, - 'mm/dd/yyyy' => self::DATE_FORMAT, - 'dd/mm/yyyy' => self::DATE_FORMAT, - 'yyyy/mm/dd' => self::DATE_FORMAT, - 'mm/yyyy' => self::DATE_FORMAT, - - 'hh AM/PM' => self::TIME_FORMAT, - 'hh:mm AM/PM' => self::TIME_FORMAT, - 'hh:mm:ss AM/PM' => self::TIME_FORMAT, - 'hh' => self::TIME_FORMAT, - 'hh:mm' => self::TIME_FORMAT, - 'hh:mm:ss' => self::TIME_FORMAT, - '[hh]:mm:ss' => self::TIME_FORMAT, - 'mm:ss' => self::TIME_FORMAT, - ]; - - /** @var string|int|null nom de la feuille depuis laquelle lire */ - const WSNAME = null; - - function __construct($input, ?array $params=null) { - parent::__construct($input, $params); - $this->wsname = $params["wsname"] ?? static::WSNAME; - } - - protected $wsname; - - /** - * @param string|int|null $wsname - */ - function setWsname($wsname): self { - $this->wsname = $wsname; - return $this; - } - - function getIterator() { - $ss = IOFactory::load($this->input); - $ws = wsutils::get_ws($this->wsname, $ss); - - [$nbCols, $nbRows] = wsutils::compute_max_coords($ws); - $this->isrc = $this->idest = 0; - for ($nrow = 1; $nrow <= $nbRows; $nrow++) { - $row = []; - for ($ncol = 1; $ncol <= $nbCols; $ncol++) { - if ($ws->cellExistsByColumnAndRow($ncol, $nrow)) { - $cell = $ws->getCellByColumnAndRow($ncol, $nrow); - $col = $cell->getValue(); - if ($col instanceof RichText) { - $col = $col->getPlainText(); - } else { - $dataType = $cell->getDataType(); - if ($dataType == DataType::TYPE_NUMERIC || $dataType == DataType::TYPE_FORMULA) { - # si c'est un format date, le forcer à une valeur standard - $origFormatCode = $cell->getStyle()->getNumberFormat()->getFormatCode(); - if (strpbrk($origFormatCode, "ymdhs") !== false) { - $formatCode = $origFormatCode; - $formatCode = preg_replace('/y+/', "yyyy", $formatCode); - $formatCode = preg_replace('/m+/', "mm", $formatCode); - $formatCode = preg_replace('/d+/', "dd", $formatCode); - $formatCode = preg_replace('/h+/', "hh", $formatCode); - $formatCode = preg_replace('/s+/', "ss", $formatCode); - $formatCode = preg_replace('/-+/', "/", $formatCode); - $formatCode = preg_replace('/\\\\ /', " ", $formatCode); - $formatCode = preg_replace('/;@$/', "", $formatCode); - $formatCode = cl::get(self::FORMAT_MAPPINGS, $formatCode, $formatCode); - if ($formatCode !== $origFormatCode) { - $cell->getStyle()->getNumberFormat()->setFormatCode($formatCode); - } - } - } - $col = $cell->getFormattedValue(); - $this->verifixCol($col); - } - } else { - $col = null; - } - $row[] = $col; - } - if ($this->cook($row)) { - yield $row; - $this->idest++; - } - $this->isrc++; - } - } }