Skip to content

Commit

Permalink
#313 : PowerPoint2007 Writer : Support for custom document properties
Browse files Browse the repository at this point in the history
  • Loading branch information
Progi1984 committed Aug 2, 2021
1 parent a0c665c commit 903b04c
Show file tree
Hide file tree
Showing 5 changed files with 283 additions and 2 deletions.
2 changes: 2 additions & 0 deletions docs/changes/1.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
- ODPresentation Writer
- PowerPoint2007 Reader
- PowerPoint2007 Writer
- Support for custom document properties - @Progi1984 GH-313
- PowerPoint2007 Writer

## Project Management
- Migrated from Travis CI to Github Actions - @Progi1984 GH-635
Expand Down
112 changes: 111 additions & 1 deletion src/PhpPresentation/DocumentProperties.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@
*/
class DocumentProperties
{
/** Constants */
const PROPERTY_TYPE_BOOLEAN = 'b';
const PROPERTY_TYPE_INTEGER = 'i';
const PROPERTY_TYPE_FLOAT = 'f';
const PROPERTY_TYPE_DATE = 'd';
const PROPERTY_TYPE_STRING = 's';
const PROPERTY_TYPE_UNKNOWN = 'u';

/**
* Creator.
*
Expand Down Expand Up @@ -94,7 +102,14 @@ class DocumentProperties
private $company;

/**
* Create a new \PhpOffice\PhpPresentation\DocumentProperties.
* Custom Properties.
*
* @var array<string, string>
*/
private $customProperties = [];

/**
* Create a new \PhpOffice\PhpPresentation\DocumentProperties
*/
public function __construct()
{
Expand Down Expand Up @@ -356,4 +371,99 @@ public function setCompany($pValue = '')

return $this;
}

/**
* Get a List of Custom Property Names.
*
* @return array of string
*/
public function getCustomProperties()
{
return array_keys($this->customProperties);
}

/**
* Check if a Custom Property is defined.
*
* @param string $propertyName
*
* @return bool
*/
public function isCustomPropertySet($propertyName)
{
return isset($this->customProperties[$propertyName]);
}

/**
* Get a Custom Property Value.
*
* @param string $propertyName
*
* @return string
*/
public function getCustomPropertyValue($propertyName)
{
if (isset($this->customProperties[$propertyName])) {
return $this->customProperties[$propertyName]['value'];
}
return null;
}

/**
* Get a Custom Property Type.
*
* @param string $propertyName
*
* @return string
*/
public function getCustomPropertyType($propertyName)
{
if (isset($this->customProperties[$propertyName])) {
return $this->customProperties[$propertyName]['type'];
}
return null;
}

/**
* Set a Custom Property.
*
* @param string $propertyName
* @param mixed $propertyValue
* @param string $propertyType
* 'i' : Integer
* 'f' : Floating Point
* 's' : String
* 'd' : Date/Time
* 'b' : Boolean
*
* @return $this
*/
public function setCustomProperty($propertyName, $propertyValue = '', $propertyType = null)
{
if (($propertyType === null)
|| (!in_array($propertyType, array(
self::PROPERTY_TYPE_INTEGER,
self::PROPERTY_TYPE_FLOAT,
self::PROPERTY_TYPE_STRING,
self::PROPERTY_TYPE_DATE,
self::PROPERTY_TYPE_BOOLEAN,
)))) {
if ($propertyValue === null) {
$propertyType = self::PROPERTY_TYPE_STRING;
} elseif (is_float($propertyValue)) {
$propertyType = self::PROPERTY_TYPE_FLOAT;
} elseif (is_int($propertyValue)) {
$propertyType = self::PROPERTY_TYPE_INTEGER;
} elseif (is_bool($propertyValue)) {
$propertyType = self::PROPERTY_TYPE_BOOLEAN;
} else {
$propertyType = self::PROPERTY_TYPE_STRING;
}
}
$this->customProperties[$propertyName] = [
'value' => $propertyValue,
'type' => $propertyType,
];
return $this;
}
}
37 changes: 36 additions & 1 deletion src/PhpPresentation/Writer/PowerPoint2007/DocPropsCustom.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PhpOffice\PhpPresentation\Writer\PowerPoint2007;

use PhpOffice\Common\XMLWriter;
use PhpOffice\PhpPresentation\DocumentProperties;

class DocPropsCustom extends AbstractDecoratorWriter
{
Expand All @@ -13,6 +14,9 @@ class DocPropsCustom extends AbstractDecoratorWriter
*/
public function render()
{
// Variables
$pId = 0;

// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);

Expand All @@ -28,7 +32,7 @@ public function render()
// property
$objWriter->startElement('property');
$objWriter->writeAttribute('fmtid', '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}');
$objWriter->writeAttribute('pid', 2);
$objWriter->writeAttribute('pid', (++$pId) * 2);
$objWriter->writeAttribute('name', '_MarkAsFinal');

// property > vt:bool
Expand All @@ -38,6 +42,37 @@ public function render()
$objWriter->endElement();
}

$oDocumentProperties = $this->oPresentation->getDocumentProperties();
foreach ($oDocumentProperties->getCustomProperties() as $customProperty) {
$propertyValue = $oDocumentProperties->getCustomPropertyValue($customProperty);
$propertyType = $oDocumentProperties->getCustomPropertyType($customProperty);

$objWriter->startElement('property');
$objWriter->writeAttribute('fmtid', '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}');
$objWriter->writeAttribute('pid', (++$pId) * 2);
$objWriter->writeAttribute('name', $customProperty);
switch ($propertyType) {
case DocumentProperties::PROPERTY_TYPE_INTEGER:
$objWriter->writeElement('vt:i4', $propertyValue);
break;
case DocumentProperties::PROPERTY_TYPE_FLOAT:
$objWriter->writeElement('vt:r8', $propertyValue);
break;
case DocumentProperties::PROPERTY_TYPE_BOOLEAN:
$objWriter->writeElement('vt:bool', ($propertyValue) ? 'true' : 'false');
break;
case DocumentProperties::PROPERTY_TYPE_DATE:
$objWriter->startElement('vt:filetime');
$objWriter->writeRaw(date(DATE_W3C, $propertyValue));
$objWriter->endElement();
break;
default:
$objWriter->writeElement('vt:lpwstr', $propertyValue);
break;
}
$objWriter->endElement();
}

// > Properties
$objWriter->endElement();

Expand Down
54 changes: 54 additions & 0 deletions tests/PhpPresentation/Tests/DocumentPropertiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,58 @@ public function testGetSetNull(): void
$this->assertEquals($time, $object->$get());
}
}

public function testCustomProperties()
{
$valueTime = time();

$object = new DocumentProperties();
$this->assertInternalType('array', $object->getCustomProperties());
$this->assertCount(0, $object->getCustomProperties());
$this->assertFalse($object->isCustomPropertySet('pName'));
$this->assertNull($object->getCustomPropertyType('pName'));
$this->assertNull($object->getCustomPropertyValue('pName'));

$this->assertInstanceOf('PhpOffice\\PhpPresentation\\DocumentProperties', $object->setCustomProperty('pName', 'pValue', null));
$this->assertCount(1, $object->getCustomProperties());
$this->assertTrue($object->isCustomPropertySet('pName'));
$this->assertEquals(DocumentProperties::PROPERTY_TYPE_STRING , $object->getCustomPropertyType('pName'));
$this->assertEquals('pValue' , $object->getCustomPropertyValue('pName'));

$this->assertInstanceOf('PhpOffice\\PhpPresentation\\DocumentProperties', $object->setCustomProperty('pName', 2, null));
$this->assertCount(1, $object->getCustomProperties());
$this->assertTrue($object->isCustomPropertySet('pName'));
$this->assertEquals(DocumentProperties::PROPERTY_TYPE_INTEGER , $object->getCustomPropertyType('pName'));
$this->assertEquals(2 , $object->getCustomPropertyValue('pName'));

$this->assertInstanceOf('PhpOffice\\PhpPresentation\\DocumentProperties', $object->setCustomProperty('pName', 2.1, null));
$this->assertCount(1, $object->getCustomProperties());
$this->assertTrue($object->isCustomPropertySet('pName'));
$this->assertEquals(DocumentProperties::PROPERTY_TYPE_FLOAT , $object->getCustomPropertyType('pName'));
$this->assertEquals(2.1 , $object->getCustomPropertyValue('pName'));

$this->assertInstanceOf('PhpOffice\\PhpPresentation\\DocumentProperties', $object->setCustomProperty('pName', true, null));
$this->assertCount(1, $object->getCustomProperties());
$this->assertTrue($object->isCustomPropertySet('pName'));
$this->assertEquals(DocumentProperties::PROPERTY_TYPE_BOOLEAN , $object->getCustomPropertyType('pName'));
$this->assertEquals(true , $object->getCustomPropertyValue('pName'));

$this->assertInstanceOf('PhpOffice\\PhpPresentation\\DocumentProperties', $object->setCustomProperty('pName', null, null));
$this->assertCount(1, $object->getCustomProperties());
$this->assertTrue($object->isCustomPropertySet('pName'));
$this->assertEquals(DocumentProperties::PROPERTY_TYPE_STRING , $object->getCustomPropertyType('pName'));
$this->assertEquals(null , $object->getCustomPropertyValue('pName'));

$this->assertInstanceOf('PhpOffice\\PhpPresentation\\DocumentProperties', $object->setCustomProperty('pName', $valueTime, DocumentProperties::PROPERTY_TYPE_DATE));
$this->assertCount(1, $object->getCustomProperties());
$this->assertTrue($object->isCustomPropertySet('pName'));
$this->assertEquals(DocumentProperties::PROPERTY_TYPE_DATE , $object->getCustomPropertyType('pName'));
$this->assertEquals($valueTime , $object->getCustomPropertyValue('pName'));

$this->assertInstanceOf('PhpOffice\\PhpPresentation\\DocumentProperties', $object->setCustomProperty('pName', (string)$valueTime, DocumentProperties::PROPERTY_TYPE_UNKNOWN));
$this->assertCount(1, $object->getCustomProperties());
$this->assertTrue($object->isCustomPropertySet('pName'));
$this->assertEquals(DocumentProperties::PROPERTY_TYPE_STRING, $object->getCustomPropertyType('pName'));
$this->assertEquals($valueTime , $object->getCustomPropertyValue('pName'));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace PhpPresentation\Tests\Writer\PowerPoint2007;

use PhpOffice\PhpPresentation\DocumentProperties;
use PhpOffice\PhpPresentation\Tests\PhpPresentationTestCase;

class DocPropsCustomTest extends PhpPresentationTestCase
Expand Down Expand Up @@ -33,4 +34,83 @@ public function testMarkAsFinalFalse(): void
$this->assertZipXmlElementNotExists('docProps/custom.xml', '/Properties/property[@name="_MarkAsFinal"]');
$this->assertIsSchemaECMA376Valid();
}

public function testCustomPropertiesBoolean()
{
$this->oPresentation->getDocumentProperties()->setCustomProperty('pName', false, null);

$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]/vt:bool');
$this->assertZipXmlElementEquals('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]/vt:bool', 'false');
}

public function testCustomPropertiesDate()
{
$value = time();
$this->oPresentation->getDocumentProperties()->setCustomProperty('pName', $value, DocumentProperties::PROPERTY_TYPE_DATE);

$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]/vt:filetime');
$this->assertZipXmlElementEquals('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]/vt:filetime', date(DATE_W3C, $value));
}

public function testCustomPropertiesFloat()
{
$this->oPresentation->getDocumentProperties()->setCustomProperty('pName', 2.1, null);

$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]/vt:r8');
$this->assertZipXmlElementEquals('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]/vt:r8', 2.1);
}

public function testCustomPropertiesInteger()
{
$this->oPresentation->getDocumentProperties()->setCustomProperty('pName', 2, null);

$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]/vt:i4');
$this->assertZipXmlElementEquals('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]/vt:i4', 2);
}

public function testCustomPropertiesNull()
{
$this->oPresentation->getDocumentProperties()->setCustomProperty('pName', null, null);

$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]/vt:lpwstr');
$this->assertZipXmlElementEquals('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]/vt:lpwstr', '');
}

public function testCustomPropertiesString()
{
$this->oPresentation->getDocumentProperties()->setCustomProperty('pName', 'pValue', null);

$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]/vt:lpwstr');
$this->assertZipXmlElementEquals('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]/vt:lpwstr', 'pValue');
}

public function testCustomPropertiesUnknown()
{
$value = time();
$this->oPresentation->getDocumentProperties()->setCustomProperty('pName', (string)$value, DocumentProperties::PROPERTY_TYPE_UNKNOWN);

$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]');
$this->assertZipXmlElementExists('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]/vt:lpwstr');
$this->assertZipXmlElementEquals('docProps/custom.xml', '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="pName"]/vt:lpwstr', $value);
}
}

0 comments on commit 903b04c

Please sign in to comment.