Initial work on Reading Conditional Formatting from Xls files
This commit is contained in:
parent
09cf6abded
commit
cb5a451aaf
|
|
@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
|||
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
|
||||
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
|
||||
use PhpOffice\PhpSpreadsheet\NamedRange;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xls\ConditionalFormatting;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xls\Style\CellFont;
|
||||
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\CodePage;
|
||||
|
|
@ -142,6 +143,8 @@ class Xls extends BaseReader
|
|||
const XLS_TYPE_SHEETLAYOUT = 0x0862;
|
||||
const XLS_TYPE_XFEXT = 0x087d;
|
||||
const XLS_TYPE_PAGELAYOUTVIEW = 0x088b;
|
||||
const XLS_TYPE_CFHEADER = 0x01b0;
|
||||
const XLS_TYPE_CFRULE = 0x01b1;
|
||||
const XLS_TYPE_UNKNOWN = 0xffff;
|
||||
|
||||
// Encryption type
|
||||
|
|
@ -1031,6 +1034,14 @@ class Xls extends BaseReader
|
|||
case self::XLS_TYPE_DATAVALIDATION:
|
||||
$this->readDataValidation();
|
||||
|
||||
break;
|
||||
case self::XLS_TYPE_CFHEADER:
|
||||
$this->readCFHeader();
|
||||
|
||||
break;
|
||||
case self::XLS_TYPE_CFRULE:
|
||||
$this->readCFRule();
|
||||
|
||||
break;
|
||||
case self::XLS_TYPE_SHEETLAYOUT:
|
||||
$this->readSheetLayout();
|
||||
|
|
@ -7921,4 +7932,128 @@ class Xls extends BaseReader
|
|||
{
|
||||
return $this->mapCellStyleXfIndex;
|
||||
}
|
||||
|
||||
private function readCFHeader(): void
|
||||
{
|
||||
var_dump('FOUND CF HEADER');
|
||||
$length = self::getUInt2d($this->data, $this->pos + 2);
|
||||
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
|
||||
|
||||
// move stream pointer forward to next record
|
||||
$this->pos += 4 + $length;
|
||||
|
||||
if ($this->readDataOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
// offset: 0; size: 2; Rule Count
|
||||
$ruleCount = self::getUInt2d($recordData, 0);
|
||||
|
||||
// offset: var; size: var; cell range address list with
|
||||
$cellRangeAddressList = ($this->version == self::XLS_BIFF8)
|
||||
? $this->readBIFF8CellRangeAddressList(substr($recordData, 12))
|
||||
: $this->readBIFF5CellRangeAddressList(substr($recordData, 12));
|
||||
$cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
|
||||
|
||||
var_dump($ruleCount, $cellRangeAddresses);
|
||||
}
|
||||
|
||||
private function readCFRule(): void
|
||||
{
|
||||
var_dump('FOUND CF RULE');
|
||||
$length = self::getUInt2d($this->data, $this->pos + 2);
|
||||
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
|
||||
|
||||
// move stream pointer forward to next record
|
||||
$this->pos += 4 + $length;
|
||||
|
||||
if ($this->readDataOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
// offset: 0; size: 2; Options
|
||||
$cfRule = self::getUInt2d($recordData, 0);
|
||||
|
||||
// bit: 8-15; mask: 0x00FF; type
|
||||
$type = (0x00FF & $cfRule) >> 0;
|
||||
$type = ConditionalFormatting::type($type);
|
||||
|
||||
// bit: 0-7; mask: 0xFF00; type
|
||||
$operator = (0xFF00 & $cfRule) >> 8;
|
||||
$operator = ConditionalFormatting::operator($operator);
|
||||
|
||||
if ($type === null || $operator === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// offset: 2; size: 2; Size1
|
||||
$size1 = self::getUInt2d($recordData, 2);
|
||||
|
||||
// offset: 4; size: 2; Size2
|
||||
$size2 = self::getUInt2d($recordData, 4);
|
||||
|
||||
// offset: 6; size: 4; Options
|
||||
$options = self::getInt4d($recordData, 6);
|
||||
|
||||
$hasFontRecord = (bool) ((0x04000000 & $options) >> 26);
|
||||
$hasAlignmentRecord = (bool) ((0x08000000 & $options) >> 27);
|
||||
$hasBorderRecord = (bool) ((0x10000000 & $options) >> 28);
|
||||
$hasFillRecord = (bool) ((0x20000000 & $options) >> 29);
|
||||
$hasProtectionRecord = (bool) ((0x40000000 & $options) >> 30);
|
||||
|
||||
$offset = 12;
|
||||
|
||||
if ($hasFontRecord === true) {
|
||||
$offset += 118;
|
||||
}
|
||||
|
||||
if ($hasAlignmentRecord === true) {
|
||||
$offset += 8;
|
||||
}
|
||||
|
||||
if ($hasBorderRecord === true) {
|
||||
$offset += 8;
|
||||
}
|
||||
|
||||
if ($hasFillRecord === true) {
|
||||
$offset += 4;
|
||||
}
|
||||
|
||||
if ($hasProtectionRecord === true) {
|
||||
$offset += 2;
|
||||
}
|
||||
|
||||
var_dump($type, $operator);
|
||||
|
||||
if ($size1 > 0) {
|
||||
$formula1 = $this->readCFFormula($recordData, $offset, $size1);
|
||||
if ($formula1 === null) {
|
||||
return;
|
||||
}
|
||||
var_dump($formula1);
|
||||
|
||||
$offset += $size1;
|
||||
}
|
||||
|
||||
if ($size2 > 0) {
|
||||
$formula2 = $this->readCFFormula($recordData, $offset, $size2);
|
||||
if ($formula2 === null) {
|
||||
return;
|
||||
}
|
||||
var_dump($formula2);
|
||||
}
|
||||
}
|
||||
|
||||
private function readCFFormula(string $recordData, int $offset, int $size): ?string
|
||||
{
|
||||
try {
|
||||
$formula = substr($recordData, $offset, $size);
|
||||
$formula = pack('v', $size) . $formula; // prepend the length
|
||||
|
||||
return $this->getFormulaFromStructure($formula);
|
||||
} catch (PhpSpreadsheetException $e) {
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
|
||||
class ConditionalFormatting
|
||||
{
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private static $types = [
|
||||
0x01 => Conditional::CONDITION_CELLIS,
|
||||
0x02 => Conditional::CONDITION_EXPRESSION,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private static $operators = [
|
||||
0x00 => Conditional::OPERATOR_NONE,
|
||||
0x01 => Conditional::OPERATOR_BETWEEN,
|
||||
0x02 => Conditional::OPERATOR_NOTBETWEEN,
|
||||
0x03 => Conditional::OPERATOR_EQUAL,
|
||||
0x04 => Conditional::OPERATOR_NOTEQUAL,
|
||||
0x05 => Conditional::OPERATOR_GREATERTHAN,
|
||||
0x06 => Conditional::OPERATOR_LESSTHAN,
|
||||
0x07 => Conditional::OPERATOR_GREATERTHANOREQUAL,
|
||||
0x08 => Conditional::OPERATOR_LESSTHANOREQUAL,
|
||||
];
|
||||
|
||||
public static function type(int $type): ?string
|
||||
{
|
||||
if (isset(self::$types[$type])) {
|
||||
return self::$types[$type];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function operator(int $operator): ?string
|
||||
{
|
||||
if (isset(self::$operators[$operator])) {
|
||||
return self::$operators[$operator];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xls;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xls;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ConditionalFormattingTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var Worksheet
|
||||
*/
|
||||
protected $sheet;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$filename = 'tests/data/Reader/XLS/CF_Basic_Comparisons.xls';
|
||||
$reader = new Xls();
|
||||
$spreadsheet = $reader->load($filename);
|
||||
$this->sheet = $spreadsheet->getActiveSheet();
|
||||
}
|
||||
|
||||
public function testReadConditionalFormatting(): void
|
||||
{
|
||||
$hasConditionalStyles = $this->sheet->conditionalStylesExists('A2:E5');
|
||||
self::assertTrue($hasConditionalStyles);
|
||||
$onditionalStyles = $this->sheet->getConditionalStyles('A2:E5');
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Loading…
Reference in New Issue