diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 000000000..37ffdfd22 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,16 @@ +engines: + phpcodesniffer: + enabled: true + phpmd: + enabled: true + duplication: + enabled: true + config: + languages: + - php +ratings: + paths: + - src/**/* + - tests/** +exclude_paths: +- vendor/**/* diff --git a/.travis.yml b/.travis.yml index dc2e11574..5a6be4b4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,6 @@ php: matrix: allow_failures: - - php: 7.0 - php: hhvm env: @@ -22,6 +21,8 @@ addons: apt: packages: - graphviz + code_climate: + repo_token: 9d5714bfd25acc27c449c2d71cfb444351a3b91c59ed1a5dc3a50732d2bf45f0 before_script: ## Composer @@ -51,6 +52,8 @@ after_script: ## Scrutinizer - wget https://scrutinizer-ci.com/ocular.phar - php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml + ## CodeClimate + - ./vendor/bin/test-reporter notifications: webhooks: diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c05729e2..811bd2179 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,43 @@ # Changelog + +## 0.7.0 - 2016-09-12 + +### Bugfix +- Fixed the image project - @mvargasmoran GH-177 +- PowerPoint2007 Writer : Bugfix for printing slide notes - @JewrassicPark @Progi1984 GH-179 + +### Changes +- PhpOffice\PhpPresentation\Writer\ODPresentation : Move to Design Pattern Decorator - @Progi1984 +- PhpOffice\PhpPresentation\Writer\PowerPoint2007 : Move to Design Pattern Decorator - @Progi1984 +- PhpOffice\PhpPresentation\Shape\Type\AbstracType\getData has been deprecated for PhpOffice\PhpPresentation\Shape\Type\AbstracType\getSeries - @Progi1984 GH-169 +- PhpOffice\PhpPresentation\Shape\Type\AbstracType\setData has been deprecated for PhpOffice\PhpPresentation\Shape\Type\AbstracType\setSeries - @Progi1984 GH-169 +- Added documentation for chart series (font, outline, marker) - @Progi1984 GH-169 +- Internal Structure for Drawing Shape - @Progi1984 GH-192 +- Documentation about manual installation - @danielbair GH-254 + +### Features +- ODPresentation & PowerPoint2007 Writer : Add support for Comment - @Progi1984 GH-116 +- ODPresentation & PowerPoint2007 Writer : Thumbnail of the presentation - @Progi1984 GH-125 +- ODPresentation & PowerPoint2007 Writer : Add support for Gridlines in Chart - @Progi1984 GH-129 +- ODPresentation & PowerPoint2007 Writer : Support for images in base 64 - @Progi1984 GH-168 +- ODPresentation & PowerPoint2007 Writer : Marker of Series in Line & Scatter chart is customizable - @Progi1984 GH-169 +- ODPresentation & PowerPoint2007 Writer : Outline of Series in Line & Scatter chart is customizable - @Progi1984 GH-169 +- ODPresentation & PowerPoint2007 & Serialized Writer : Support for Zip Adapter - @Progi1984 GH-176 +- ODPresentation & PowerPoint2007 Writer : language property to TextElement - @skrajewski & @Progi1984 GH-180 +- ODPresentation & PowerPoint2007 Writer : Add Font Support For Chart Axis - @jrking4 GH-186 +- ODPresentation & PowerPoint2007 Writer : Support for video - @Progi1984 GH-123 +- ODPresentation & PowerPoint2007 Writer : Support for Visibility for slides - @Progi1984 +- PowerPoint2007 Reader : Layout Management - @vincentKool @Progi1984 GH-161 +- PowerPoint2007 Reader : Slide size - @loverslcn @Progi1984 GH-246 +- PowerPoint2007 Reader : Bullet Color - @Progi1984 GH-257 +- PowerPoint2007 Reader : Line Spacing - @Progi1984 GH-257 +- PowerPoint2007 Writer : Presentation with predefined View Type - @Progi1984 GH-120 +- PowerPoint2007 Writer : Implement alpha channel to Fills - @Dayjo GH-203 / @Progi1984 GH-215 +- PowerPoint2007 Writer : Implement Animations - @JewrassicPark GH-214 / @Progi1984 GH-217 +- PowerPoint2007 Writer : Layout Management - @vincentKool @Progi1984 GH-161 +- PowerPoint2007 Writer : Bullet Color - @piotrbelina GH-249 +- PowerPoint2007 Writer : Line Spacing - @piotrbelina GH-249 + ## 0.6.0 - 2016-01-24 ### Bugfix diff --git a/README.md b/README.md index c690dce6f..4a831b23c 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ -# ![PHPPresentation](https://github.com/PHPOffice/PHPPresentation/raw/master/docs/images/PHPPowerPointLogo.png "PHPPresentation") +# ![PHPPresentation](https://raw.githubusercontent.com/mvargasmoran/PHPPresentation/develop/docs/images/PHPPresentationLogo.png "PHPPresentation") -[![Latest Stable Version](https://poser.pugx.org/phpoffice/phppowerpoint/v/stable.png)](https://packagist.org/packages/phpoffice/phppowerpoint) +[![Latest Stable Version](https://poser.pugx.org/phpoffice/phppresentation/v/stable.png)](https://packagist.org/packages/phpoffice/phppresentation) [![Build Status](https://travis-ci.org/PHPOffice/PHPPresentation.svg?branch=master)](https://travis-ci.org/PHPOffice/PHPPresentation) -[![Code Quality](https://scrutinizer-ci.com/g/PHPOffice/PHPPowerPoint/badges/quality-score.png?s=b5997ce59ac2816b4514f3a38de9900f6d492c1d)](https://scrutinizer-ci.com/g/PHPOffice/PHPPowerPoint/) -[![Code Coverage](https://scrutinizer-ci.com/g/PHPOffice/PHPPowerPoint/badges/coverage.png?s=742a98745725c562955440edc8d2c39d7ff5ae25)](https://scrutinizer-ci.com/g/PHPOffice/PHPPowerPoint/) -[![Total Downloads](https://poser.pugx.org/phpoffice/phppowerpoint/downloads.png)](https://packagist.org/packages/phpoffice/phppowerpoint) -[![License](https://poser.pugx.org/phpoffice/phppowerpoint/license.png)](https://packagist.org/packages/phpoffice/phppowerpoint) -[![Join the chat at https://gitter.im/PHPOffice/PHPPowerPoint](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/PHPOffice/PHPPowerPoint) +[![Code Climate](https://codeclimate.com/github/PHPOffice/PHPPresentation/badges/gpa.svg)](https://codeclimate.com/github/PHPOffice/PHPPresentation) +[![Test Coverage](https://codeclimate.com/github/PHPOffice/PHPPresentation/badges/coverage.svg)](https://codeclimate.com/github/PHPOffice/PHPPresentation/coverage) +[![Total Downloads](https://poser.pugx.org/phpoffice/phppresentation/downloads.png)](https://packagist.org/packages/phpoffice/phppresentation) +[![License](https://poser.pugx.org/phpoffice/phppresentation/license.png)](https://packagist.org/packages/phpoffice/phppresentation) +[![Join the chat at https://gitter.im/PHPOffice/PHPPresentation](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/PHPOffice/PHPPresentation) PHPPresentation is a library written in pure PHP that provides a set of classes to write to different presentation file formats, i.e. Microsoft [Office Open XML](http://en.wikipedia.org/wiki/Office_Open_XML) (OOXML or OpenXML) or OASIS [Open Document Format for Office Applications](http://en.wikipedia.org/wiki/OpenDocument) (OpenDocument or ODF). -PHPPresentation is an open source project licensed under the terms of [LGPL version 3](https://github.com/PHPOffice/PHPPresentation/blob/develop/COPYING.LESSER). PHPPresentation is aimed to be a high quality software product by incorporating [continuous integration](https://travis-ci.org/PHPOffice/PHPPresentation) and [unit testing](http://phpoffice.github.io/PHPPresentation/coverage/develop/). You can learn more about PHPPresentation by reading the [Developers' Documentation](http://phppowerpoint.readthedocs.org/) and the [API Documentation](http://phpoffice.github.io/PHPPresentation/docs/develop/). +PHPPresentation is an open source project licensed under the terms of [LGPL version 3](https://github.com/PHPOffice/PHPPresentation/blob/develop/COPYING.LESSER). PHPPresentation is aimed to be a high quality software product by incorporating [continuous integration](https://travis-ci.org/PHPOffice/PHPPresentation) and [unit testing](http://phpoffice.github.io/PHPPresentation/coverage/develop/). You can learn more about PHPPresentation by reading the [Developers' Documentation](http://phppresentation.readthedocs.org/) and the [API Documentation](http://phpoffice.github.io/PHPPresentation/docs/develop/). Read more about PHPPresentation: @@ -20,7 +20,7 @@ Read more about PHPPresentation: - [Installation](#installation) - [Getting started](#getting-started) - [Contributing](#contributing) -- [Developers' Documentation](http://phppowerpoint.readthedocs.org/) +- [Developers' Documentation](http://phppresentation.readthedocs.org/) - [API Documentation](http://phpoffice.github.io/PHPPresentation/docs/master/) ### Features @@ -48,6 +48,8 @@ PHPPresentation requires the following: ### Installation +#### Composer method + It is recommended that you install the PHPPresentation library [through composer](http://getcomposer.org/). To do so, add the following lines to your ``composer.json``. @@ -59,14 +61,25 @@ the following lines to your ``composer.json``. } ``` -Alternatively, you can download the latest release from the [releases page](https://github.com/PHPOffice/PHPPresentation/releases). -In this case, you will have to register the autoloader. Register autoloading is required only if you do not use composer in your project. +#### Manual download method + +Alternatively, you can download the latest release from the [releases page](https://github.com/PHPOffice/PHPPresentation/releases). +In this case, you will have to register the autoloader. +(Register autoloading is required only if you do not use composer in your project.) ```php require_once 'path/to/PhpPresentation/src/PhpPresentation/Autoloader.php'; \PhpOffice\PhpPresentation\Autoloader::register(); ``` +You will also need to download the latest PHPOffice/Common release from its [releases page](https://github.com/PHPOffice/Common/releases). +And you will also have to register its autoloader, too. + +```php +require_once 'path/to/PhpOffice/Common/src/Common/Autoloader.php'; +\PhpOffice\Common\Autoloader::register(); +``` + ## Getting started The following is a basic usage example of the PHPPresentation library. @@ -75,6 +88,8 @@ The following is a basic usage example of the PHPPresentation library. // with your own install require_once 'src/PhpPresentation/Autoloader.php'; \PhpOffice\PhpPresentation\Autoloader::register(); +require_once 'src/Common/Autoloader.php'; +\PhpOffice\Common\Autoloader::register(); // with Composer require_once 'vendor/autoload.php'; @@ -119,7 +134,7 @@ $oWriterODP = IOFactory::createWriter($objPHPPowerPoint, 'ODPresentation'); $oWriterODP->save(__DIR__ . "/sample.odp"); ``` -More examples are provided in the [samples folder](samples/). You can also read the [Developers' Documentation](http://phppowerpoint.readthedocs.org/) and the [API Documentation](http://phpoffice.github.io/PHPPresentation/docs/master/) for more detail. +More examples are provided in the [samples folder](samples/). You can also read the [Developers' Documentation](http://phppresentation.readthedocs.org/) and the [API Documentation](http://phpoffice.github.io/PHPPresentation/docs/master/) for more detail. ## Contributing diff --git a/VERSION b/VERSION index 1d0ba9ea1..a918a2aa1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.0 +0.6.0 diff --git a/composer.json b/composer.json index 23a9b0d9f..ce4852a68 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "php": ">=5.3.0", "ext-xml": "*", "ext-zip": "*", - "phpoffice/common": "0.2.*" + "phpoffice/common": "0.2.*" }, "require-dev": { "phpunit/phpunit": "3.7.*", @@ -30,7 +30,8 @@ "phpmd/phpmd": "2.*", "sebastian/phpcpd": "2.*", "phploc/phploc": "2.*", - "squizlabs/php_codesniffer": "2.*" + "squizlabs/php_codesniffer": "2.*", + "codeclimate/php-test-reporter": "dev-master" }, "suggest": { "ext-gd2": "Required to add images" diff --git a/docs/credits.rst b/docs/credits.rst index 379ff303d..85987b750 100644 --- a/docs/credits.rst +++ b/docs/credits.rst @@ -3,4 +3,6 @@ Credits ======= - Images from chart page come from the `LibreOffice Core `. \ No newline at end of file + Images from chart page come from the `LibreOffice Core `. + + Some definitions come from the `Office Open XML `. \ No newline at end of file diff --git a/docs/general.rst b/docs/general.rst index 6cf0b5b54..d24c884e7 100644 --- a/docs/general.rst +++ b/docs/general.rst @@ -53,7 +53,7 @@ Document information -------------------- You can set the document information such as title, creator, and company -name. Use the following functions: +name. Use the following functions : .. code-block:: php @@ -69,3 +69,76 @@ name. Use the following functions: $properties->setSubject('My subject'); $properties->setKeywords('my, key, word'); + +Presentation Properties +----------------------- + +You can define some properties which are relative to the presentation, like the zoom or the thumbnail. + +Comments +```````` + +You can define if the presentation display or not the comments with the method ``setCommentVisible``. + +.. code-block:: php + + $oPresentation = new PhpPresentation(); + $oProperties = $oPresentation->getPresentationProperties(); + // Get the display for comment + var_export($oProperties->isCommentVisible()); + // Output : false + // Enable the display for comment + $oProperties->setCommentVisible(true); + // Get the display for comment + var_export($oProperties->isCommentVisible()); + // Output : true + +Last View +````````` + +You can define the last view of the presentation with the method ``setLastView``. + +.. code-block:: php + + $oPresentation = new PhpPresentation(); + $oProperties = $oPresentation->getPresentationProperties(); + // Get the last view of the presentation + echo $oProperties->getZoom(); + // Output : PresentationProperties::VIEW_SLIDE + // Set the last view of the presentation + $oProperties->setLastView(PresentationProperties::VIEW_NOTES); + // Get the last view of the presentation + echo $oProperties->getZoom(); + // Output : PresentationProperties::VIEW_NOTES + +Thumbnail +````````` + +You can define the thumbnail of the presentation with the method ``setThumbnailPath``. + +.. code-block:: php + + $oPresentation = new PhpPresentation(); + $oProperties = $oPresentation->getPresentationProperties(); + // Set path of the thumbnail + $oProperties->setThumbnailPath(__DIR__.'\resources\phppowerpoint_logo.gif'); + // Get path of the thumbnail + echo $oProperties->getThumbnailPath(); + +Zoom +```` + +You can define the zoom of the presentation with the method ``setZoom``. + +.. code-block:: php + + $oPresentation = new PhpPresentation(); + $oProperties = $oPresentation->getPresentationProperties(); + // Get zoom of the presentation + echo $oProperties->getZoom(); + // Output : 1 + // Set zoom of the presentation (3 = 300%) + $oProperties->setZoom(3); + // Get zoom of the presentation + echo $oProperties->getZoom(); + // Output : 3 diff --git a/docs/images/PHPPowerPointLogo.png b/docs/images/PHPPowerPointLogo.png deleted file mode 100644 index f95372ce2..000000000 Binary files a/docs/images/PHPPowerPointLogo.png and /dev/null differ diff --git a/docs/images/PHPPresentationLogo.png b/docs/images/PHPPresentationLogo.png new file mode 100644 index 000000000..8337af883 Binary files /dev/null and b/docs/images/PHPPresentationLogo.png differ diff --git a/docs/index.rst b/docs/index.rst index 6a4465cf6..d88c933c4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,7 +4,7 @@ contain the root `toctree` directive. Welcome to PHPPresentation's documentation -======================================== +========================================== |PHPPresentation| @@ -19,12 +19,21 @@ PHPPresentation is a library written in pure PHP that provides a set of classes slides shapes styles - writersreaders + writers + readers recipes faq credits references - + +.. _slidesdocs: + +.. toctree:: + :maxdepth: 2 + :caption: Slides + + slides_animation + .. _shapesdocs: .. toctree:: @@ -32,6 +41,10 @@ PHPPresentation is a library written in pure PHP that provides a set of classes :caption: Shapes shapes_chart + shapes_comment + shapes_drawing + shapes_media + shapes_richtext shapes_table Indices and tables diff --git a/docs/installing.rst b/docs/installing.rst index 9618828a8..0bf5e657e 100644 --- a/docs/installing.rst +++ b/docs/installing.rst @@ -42,17 +42,20 @@ To install via Composer, add the following lines to your Manual install ~~~~~~~~~~~~~~ -To install manually, `download PHPPresentation package from -github `__. -Extract the package and put the contents to your machine. To use the -library, include ``src/PhpPresentation/Autoloader.php`` in your script and -invoke ``Autoloader::register``. +To install manually, `download PHPOffice\PHPPresentation package from +GitHub `__ +and `download PHPOffice\Common package from +GitHub `__. +Extract the package and put the contents to your machine. .. code-block:: php - require_once '/path/to/src/PhpPresentation/Autoloader.php'; + require_once 'path/to/PhpPresentation/src/PhpPresentation/Autoloader.php'; \PhpOffice\PhpPresentation\Autoloader::register(); + require_once 'path/to/PhpOffice/Common/src/Common/Autoloader.php'; + \PhpOffice\Common\Autoloader::register(); + Using samples ------------- diff --git a/docs/readers.rst b/docs/readers.rst new file mode 100644 index 000000000..cd860c87c --- /dev/null +++ b/docs/readers.rst @@ -0,0 +1,44 @@ +.. _writersreaders: + +Readers +======= + +ODPresentation +-------------- + +The name of the reader is ``ODPresentation``. + +.. code-block:: php + + $oWriter = IOFactory::createReader('ODPresentation'); + $oWriter->load(__DIR__ . '/sample.odp'); + +PowerPoint97 +------------ + +The name of the reader is ``PowerPoint97``. + +.. code-block:: php + + $oWriter = IOFactory::createReader('PowerPoint97'); + $oWriter->load(__DIR__ . '/sample.ppt'); + +PowerPoint2007 +-------------- + +The name of the reader is ``PowerPoint2007``. + +.. code-block:: php + + $oWriter = IOFactory::createReader('PowerPoint2007'); + $oWriter->load(__DIR__ . '/sample.pptx'); + +Serialized +---------- + +The name of the reader is ``Serialized``. + +.. code-block:: php + + $oWriter = IOFactory::createReader('Serialized'); + $oWriter->load(__DIR__ . '/sample.phppt'); diff --git a/docs/shapes.rst b/docs/shapes.rst index c892bd19c..dd21ab97d 100644 --- a/docs/shapes.rst +++ b/docs/shapes.rst @@ -27,58 +27,7 @@ Example: ->setOffsetX(170) ->setOffsetY(180); -Rich text ---------- - -Rich text shapes contain paragraphs of texts. To create a rich text shape, use ``createRichTextShape`` method of slide. - -Below are the properties that you can set for a rich text shape. - -- ``wrap`` -- ``autoFit`` -- ``fontScale`` : font scale (in percentage) when autoFit = RichText::AUTOFIT_NORMAL -- ``lnSpcReduction`` : line spacing reduction (in percentage) when autoFit = RichText::AUTOFIT_NORMAL -- ``horizontalOverflow`` -- ``verticalOverflow`` -- ``upright`` -- ``vertical`` -- ``columns`` -- ``bottomInset`` in pixels -- ``leftInset`` in pixels -- ``rightInset`` in pixels -- ``topInset`` in pixels -- ``autoShrinkHorizontal`` (boolean) -- ``autoShrinkVertical`` (boolean) - -Properties that can be set for each paragraphs are as follow. - -- ``alignment`` see *[Alignment](#alignment)* -- ``font`` see *[Font](#font)* -- ``bulletStyle`` see *[Bullet](#bullet)* - Line -------- +---- To create a line, use `createLineShape` method of slide. - -Chart -------- - -The Chart has now :ref:`its own page `. - -Drawing -------- - -To create a drawing, use `createDrawingShape` method of slide. - -.. code-block:: php - - $drawing = $slide->createDrawingShape(); - $drawing->setName('Unique name') - ->setDescription('Description of the drawing') - ->setPath('/path/to/drawing.filename'); - -Table -------- - -The Table has now :ref:`its own page `. diff --git a/docs/shapes_chart.rst b/docs/shapes_chart.rst index d86e04b68..8b0d3f999 100644 --- a/docs/shapes_chart.rst +++ b/docs/shapes_chart.rst @@ -9,10 +9,30 @@ Example: .. code-block:: php - $chartShape = $slide->createChartShape(); - + $chartShape = $slide->createChartShape(); + Parts -------- +----- + +Axis +^^^^ + +You can define gridlines (minor and major) for each axis (X & Y). +For each gridline, you can custom the width (in points), the fill type and the fill color. + +.. code-block:: php + + use \PhpOffice\PhpPresentation\Shape\Chart\Gridlines; + + $oLine = new Line(); + + $oGridLines = new Gridlines(); + $oGridLines->getOutline()->setWidth(10); + $oGridLines->getOutline()->getFill()->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_BLUE)); + + $oShape = $oSlide->createChartShape(); + $oShape->getPlotArea()->setType($oLine); + $oShape->getPlotArea()->getAxisX()->setMajorGridlines($oGridLines); Title ^^^^^ @@ -22,14 +42,56 @@ For hiding it, you define its visibility to false. .. code-block:: php - $chartShape = $slide->createChartShape(); - $oLine = new Line(); - $oShape->getPlotArea()->setType($oLine); - // Hide the title - $oShape->getTitle()->setVisible(false); - + $oLine = new Line(); + $oShape = $slide->createChartShape(); + $oShape->getPlotArea()->setType($oLine); + // Hide the title + $oShape->getTitle()->setVisible(false); + +Series +^^^^^^ + +You can custom the font of a serie. + +.. code-block:: php + $oSeries = new Series('Downloads', $seriesData); + // Define the size + $oSeries->getFont()->setSize(25); + +You can custom the marker of a serie, for Line & Scatter charts. + +.. code-block:: php + use \PhpOffice\PhpPresentation\Shape\Chart\Marker; + + $oSeries = new Series('Downloads', $seriesData); + $oMarker = $oSeries->getMarker(); + $oMarker->setSymbol(Marker::SYMBOL_DASH)->setSize(10); + +You can custom the line of a serie, for Line & Scatter charts. + +.. code-block:: php + use \PhpOffice\PhpPresentation\Style\Outline; + + $oOutline = new Outline(); + // Define the color + $oOutline->getFill()->setFillType(Fill::FILL_SOLID); + $oOutline->getFill()->setStartColor(new Color(Color::COLOR_YELLOW)); + // Define the width (in points) + $oOutline->setWidth(2); + + $oSeries = new Series('Downloads', $seriesData); + $oSeries->setOutline($oOutline); + +You can define the position of the data label. +Each position is described in `MSDN `_ + +.. code-block:: php + + $oSeries = new Series('Downloads', $seriesData); + $oSeries->setLabelPosition(Series::LABEL_INSIDEEND); + Types -------- +----- Area ^^^^ @@ -46,15 +108,15 @@ You can stack multiples series in a same chart. After adding multiples series, y .. code-block:: php - $oBarChart = new Bar(); - $oBarChart->addSeries($oSeries1); - $oBarChart->addSeries($oSeries2); - $oBarChart->addSeries($oSeries3); - $oBarChart->setBarGrouping(Bar::GROUPING_CLUSTERED); - // OR - $oBarChart->setBarGrouping(Bar::GROUPING_STACKED); - // OR - $oBarChart->setBarGrouping(Bar::GROUPING_PERCENTSTACKED); + $oBarChart = new Bar(); + $oBarChart->addSeries($oSeries1); + $oBarChart->addSeries($oSeries2); + $oBarChart->addSeries($oSeries3); + $oBarChart->setBarGrouping(Bar::GROUPING_CLUSTERED); + // OR + $oBarChart->setBarGrouping(Bar::GROUPING_STACKED); + // OR + $oBarChart->setBarGrouping(Bar::GROUPING_PERCENTSTACKED); - Bar::GROUPING_CLUSTERED .. image:: images/chart_columns_52x60.png diff --git a/docs/shapes_comment.rst b/docs/shapes_comment.rst new file mode 100644 index 000000000..6ad217b47 --- /dev/null +++ b/docs/shapes_comment.rst @@ -0,0 +1,62 @@ +.. _shapes_comment: + +Comments +======== + +To create a comment, create an object `Comment`. + +Example: + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\Comment; + + $oComment = new Comment(); + $oSlide->addShape($oComment); + +You can define text and date with setters. + +Example: + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\Comment; + + $oComment = new Comment(); + $oComment->setText('Text of the Comment'); + $oComment->setDate(time()); + $oSlide->addShape($oComment); + + +Author +------ + +For a comment, you can define the author. + +Example: + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\Comment; + use PhpOffice\PhpPresentation\Shape\Comment\Author; + + $oAuthor = new Author(); + $oComment = new Comment(); + $oComment->setAuthor($oAuthor); + $oSlide->addShape($oComment); + +You can define name and initials with setters. + +Example: + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\Comment; + use PhpOffice\PhpPresentation\Shape\Comment\Author; + + $oAuthor = new Author(); + $oAuthor->setName('Name of the author'); + $oAuthor->setInitals('Nota'); + $oComment = new Comment(); + $oComment->setAuthor($oAuthor); + $oSlide->addShape($oComment); \ No newline at end of file diff --git a/docs/shapes_drawing.rst b/docs/shapes_drawing.rst new file mode 100644 index 000000000..7777951f7 --- /dev/null +++ b/docs/shapes_drawing.rst @@ -0,0 +1,75 @@ +.. _shapes_drawing: + +Drawing +======= + +To create a drawing, you have four sources : File, GD, Base64 and ZipFile. + +File +---- + +To create a drawing, use `createDrawingShape` method of slide. + +.. code-block:: php + + $oShape = $oSlide->createDrawingShape(); + $oShape->setName('Unique name') + ->setDescription('Description of the drawing') + ->setPath('/path/to/drawing.filename'); + +It's an alias for : + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\Drawing\File; + + $oShape = new File(); + $oShape->setName('Unique name') + ->setDescription('Description of the drawing') + ->setPath('/path/to/drawing.filename'); + $oSlide->addShape($oShape); + +GD +-- + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\Drawing\Gd; + + $gdImage = @imagecreatetruecolor($width, $height); + + $oShape = new Gd(); + $oShape->setName('Sample image') + ->setDescription('Sample image') + ->setImageResource($gdImage) + ->setRenderingFunction(Drawing\Gd::RENDERING_JPEG) + ->setMimeType(Drawing\Gd::MIMETYPE_DEFAULT); + $oSlide->addShape($oShape); + + +Base64 +------ + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\Drawing\Base64; + + $oShape = new Base64(); + $oShape->setName('Sample image') + ->setDescription('Sample image') + ->setImageResource($gdImage) + ->setData('data:image/jpeg;base64,..........'); + $oSlide->addShape($oShape); + +ZipFile +------- + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\Drawing\ZipFile; + + $oShape = new ZipFile(); + $oShape->setName('Sample image') + ->setDescription('Sample image') + ->setPath('zip://myzipfile.zip#path/in/zip/img.ext') + $oSlide->addShape($oShape); diff --git a/docs/shapes_media.rst b/docs/shapes_media.rst new file mode 100644 index 000000000..ba3fddec1 --- /dev/null +++ b/docs/shapes_media.rst @@ -0,0 +1,36 @@ +.. _shapes_table: + +Media +===== + +To create a video, create an object `Media`. + +Example: + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\Media; + + $oMedia = new Media(); + $oMedia->setPath('file.wmv'); + // $oMedia->setPath('file.ogv'); + $oSlide->addShape($oMedia); + +You can define text and date with setters. + +Example: + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\Media; + + $oMedia = new Media(); + $oMedia->setName('Name of the Media'); + $oSlide->addShape($oMedia); + + +Quirks +------ + +For Windows readers, the prefered file format is WMV. +For Linux readers, the prefered file format is OGV. \ No newline at end of file diff --git a/docs/shapes_richtext.rst b/docs/shapes_richtext.rst new file mode 100644 index 000000000..e8987cb64 --- /dev/null +++ b/docs/shapes_richtext.rst @@ -0,0 +1,91 @@ +.. _shapes_richtext: + +RichText +======== + +Rich text shapes contain paragraphs of texts. To create a rich text shape, use ``createRichTextShape`` method of slide. + +Below are the properties that you can set for a rich text shape. + +- ``wrap`` +- ``autoFit`` +- ``fontScale`` : font scale (in percentage) when autoFit = RichText::AUTOFIT_NORMAL +- ``lnSpcReduction`` : line spacing reduction (in percentage) when autoFit = RichText::AUTOFIT_NORMAL +- ``horizontalOverflow`` +- ``verticalOverflow`` +- ``upright`` +- ``vertical`` +- ``columns`` +- ``bottomInset`` in pixels +- ``leftInset`` in pixels +- ``rightInset`` in pixels +- ``topInset`` in pixels +- ``autoShrinkHorizontal`` (boolean) +- ``autoShrinkVertical`` (boolean) + +Properties that can be set for each paragraphs are as follow. + +- ``alignment`` see *[Alignment](#alignment)* +- ``bulletStyle`` see *[Bullet](#bullet)* +- ``lineSpacing`` see *[LineSpacing](#linespacing)* +- ``font`` see *[Font](#font)* + + +Bullet +------ + +For a paragraph, you can define the bullet style. + +Example: + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\RichText\Paragraph; + use PhpOffice\PhpPresentation\Style\Bullet; + + $oParagraph = new Paragraph(); + $oParagraph->getBulletStyle(); + +With the bullet style, you can define the char, the font, the color and the type. + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\RichText\Paragraph; + use PhpOffice\PhpPresentation\Style\Bullet; + use PhpOffice\PhpPresentation\Style\Color; + + $oParagraph = new Paragraph(); + $oParagraph->getBulletStyle()->setBulletChar('-'); + $oParagraph->getBulletStyle()->setBulletType(Bullet::TYPE_BULLET); + $oParagraph->getBulletStyle()->setBulletColor(new Color(Color::COLOR_RED)); + + +LineSpacing +----------- + +For a paragraph, you can define the line spacing. + +Example: + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\RichText\Paragraph; + + $oParagraph = new Paragraph(); + $oParagraph->setLineSpacing(200); + $iLineSpacing = $oParagraph->getLineSpacing(); + + +Run +--- + +For a run, you can define the language. + +Example: + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\RichText\Run; + + $oRun = new Run(); + $oRun->setLanguage('fr-FR'); \ No newline at end of file diff --git a/docs/slides.rst b/docs/slides.rst index 79a9bf4e3..c1a5e4b68 100644 --- a/docs/slides.rst +++ b/docs/slides.rst @@ -6,7 +6,7 @@ Slides Slides are pages in a presentation. Slides are stored as a zero based array in ``PHPPresentation`` object. Use ``createSlide`` to create a new slide and retrieve the slide for other operation such as creating shapes for that slide. Name -------- +---- By default, a slide has not a name. You can define it with the method ``setName``. @@ -14,4 +14,16 @@ You can define it with the method ``setName``. .. code-block:: php $oSlide = $oPHPPresentation->createSlide(); - $oSlide->setName('Title of the slide'); \ No newline at end of file + $oSlide->setName('Title of the slide'); + +Visibility +---------- + +By default, a slide is visible. +You can define it with the method ``setIsVisible``. + +.. code-block:: php + + $oSlide = $oPHPPresentation->createSlide(); + $oSlide->setIsVisible(false); + var_dump($oSlide->isVisible()); \ No newline at end of file diff --git a/docs/slides_animation.rst b/docs/slides_animation.rst new file mode 100644 index 000000000..662aac38c --- /dev/null +++ b/docs/slides_animation.rst @@ -0,0 +1,19 @@ +.. _slides_animation: + +Animation +========= + +You can create multiples animations in a slide. + +.. code-block:: php + + use PhpOffice\PhpPresentation\Slide\Animation; + + $oAnimation1 = new Animation(); + $oAnimation1->addShape($oDrawing); + $oSlide->addAnimation($oAnimation1); + + $oAnimation2 = new Animation(); + $oAnimation2->addShape($oRichText); + $oSlide->addAnimation($oAnimation2); + diff --git a/docs/slides_layout.rst b/docs/slides_layout.rst new file mode 100644 index 000000000..7a43144af --- /dev/null +++ b/docs/slides_layout.rst @@ -0,0 +1,50 @@ +.. _slides_layout: + +Slides Layout +============= + +A slide is a hierarchy of three components : +- The master slide upon which the slide is based : it specifies such properties as the font styles for the title, body, and footer, placeholder positions for text and objects, bullets styles, and background ; +- The slide layout which is applied to the the slide : it permits to override what is specified in the master slide ; +- The slide itself : it contains content and formatting that is not already specified by the master slide and the slide layout + +Placeholders permit to link these three components together in order that the override is possible. + +Master slides +------------- + +You can access to all master slides with the method ``getAllMasterSlides`` or create one with ``createMasterSlide``. + +.. code-block:: php + + $arraySlideMasters = $oPHPPresentation->getAllMasterSlides(); + $oMasterSlide = $oPHPPresentation->createMasterSlide(); + +Slides Layout +------------- + +You can access to all slide layout from a master with the method ``getAllSlideLayouts`` or create one with ``createSlideLayout``. + +.. code-block:: php + + $arraySlideLayouts = $oMasterSlide->getAllSlideLayouts(); + $oSlideLayout = $oMasterSlide->createSlideLayout(); + +Placeholders +------------ + +For each master slide or slide layout, you can add any shape like on a slide. + +.. code-block:: php + + $oShape = $oMasterSlide->createChartShape(); + $oShape = $oSlideLayout->createTableShape(); + +You can define a shape as a placeholder for each level with the method ``setPlaceHolder``. +A shape defined in each level will have an override for its formatting in each level. + +.. code-block:: php + + use PhpOffice\PhpPresentation\Shape\Placeholder; + $oShape->setPlaceHolder(new Placeholder(Placeholder::PH_TYPE_TITLE)); + diff --git a/docs/writers.rst b/docs/writers.rst new file mode 100644 index 000000000..e1105290e --- /dev/null +++ b/docs/writers.rst @@ -0,0 +1,58 @@ +.. _writersreaders: + +Writers +======= + + +ODPresentation +-------------- + +The name of the writer is ``ODPresentation``. + +.. code-block:: php + + $oWriter = IOFactory::createWriter($oPhpPresentation, 'PowerPoint2007'); + $oWriter->save(__DIR__ . '/sample.pptx'); + +PowerPoint2007 +-------------- + +The name of the writer is ``PowerPoint2007``. + +.. code-block:: php + + $oWriter = IOFactory::createWriter($oPhpPresentation, 'PowerPoint2007'); + $oWriter->save(__DIR__ . '/sample.pptx'); + +You can change the ZIP Adapter for the writer. By default, the ZIP Adapter is ZipArchiveAdapter. + +.. code-block:: php + + use PhpOffice\Common\Adapter\Zip\PclZipAdapter; + use PhpOffice\Common\Adapter\Zip\ZipArchiveAdapter; + + $oWriter = IOFactory::createWriter($oPhpPresentation, 'PowerPoint2007'); + $oWriter->setZipAdapter(PclZipAdapter); + $oWriter->save(__DIR__ . '/sample.pptx'); + +Serialized +---------- + +The name of the writer is ``Serialized``. + +.. code-block:: php + + $oWriter = IOFactory::createWriter($oPhpPresentation, 'Serialized'); + $oWriter->save(__DIR__ . '/sample.phppt'); + +You can change the ZIP Adapter for the writer. By default, the ZIP Adapter is ZipArchiveAdapter. + +.. code-block:: php + + use PhpOffice\Common\Adapter\Zip\PclZipAdapter; + use PhpOffice\Common\Adapter\Zip\ZipArchiveAdapter; + + $oWriter = IOFactory::createWriter($oPhpPresentation, 'Serialized'); + $oWriter->setZipAdapter(PclZipAdapter); + $oWriter->save(__DIR__ . '/sample.phppt'); + diff --git a/docs/writersreaders.rst b/docs/writersreaders.rst deleted file mode 100644 index 9b39b54e2..000000000 --- a/docs/writersreaders.rst +++ /dev/null @@ -1,106 +0,0 @@ -.. _writersreaders: - -Writers & readers -================= - -OOXML ------ - -The package of OOXML document consists of the following files. - -- \_rels/ - - - .rels - -- docProps/ - - - app.xml - - core.xml - - custom.xml - -- word/ - - - rels/ - - - document.rels.xml - - - media/ - - theme/ - - - theme1.xml - - - document.xml - - fontTable.xml - - numbering.xml - - settings.xml - - styles.xml - - webSettings.xml - -- [Content\_Types].xml - -OpenDocument ------------- - -Package -~~~~~~~ - -The package of OpenDocument document consists of the following files. - -- META-INF/ - - - manifest.xml - -- Pictures/ -- content.xml -- meta.xml -- styles.xml - -content.xml -~~~~~~~~~~~ - -The structure of ``content.xml`` is described below. - -- office:document-content - - - office:font-facedecls - - office:automatic-styles - - office:body - - - office:text - - - draw:\* - - office:forms - - table:table - - text:list - - text:numbered-paragraph - - text:p - - text:table-of-contents - - text:section - - - office:chart - - office:image - - office:drawing - -styles.xml -~~~~~~~~~~ - -The structure of ``styles.xml`` is described below. - -- office:document-styles - - - office:styles - - office:automatic-styles - - office:master-styles - - - office:master-page - - -HTML ----- - -To be completed. - -PDF ---- - -To be completed. diff --git a/phpmd.xml.dist b/phpmd.xml.dist index 33179ad87..e920f43e5 100644 --- a/phpmd.xml.dist +++ b/phpmd.xml.dist @@ -4,11 +4,25 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd" xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"> - + + + + + + + + + + + + + + + diff --git a/samples/Sample_01_Complex.php b/samples/Sample_01_Complex.php index a5b0b7fa8..ec492a85c 100644 --- a/samples/Sample_01_Complex.php +++ b/samples/Sample_01_Complex.php @@ -15,7 +15,7 @@ // Set properties echo date('H:i:s') . ' Set properties'.EOL; -$objPHPPresentation->getProperties()->setCreator('PHPOffice') +$objPHPPresentation->getDocumentProperties()->setCreator('PHPOffice') ->setLastModifiedBy('PHPPresentation Team') ->setTitle('Sample 02 Title') ->setSubject('Sample 02 Subject') diff --git a/samples/Sample_01_Simple.php b/samples/Sample_01_Simple.php index c99bb867b..e7ebd0b9a 100644 --- a/samples/Sample_01_Simple.php +++ b/samples/Sample_01_Simple.php @@ -11,48 +11,48 @@ $objPHPPresentation = new PhpPresentation(); // Set properties -echo date('H:i:s') . ' Set properties'.EOL; -$objPHPPresentation->getProperties()->setCreator('PHPOffice') - ->setLastModifiedBy('PHPPresentation Team') - ->setTitle('Sample 01 Title') - ->setSubject('Sample 01 Subject') - ->setDescription('Sample 01 Description') - ->setKeywords('office 2007 openxml libreoffice odt php') - ->setCategory('Sample Category'); +echo date('H:i:s') . ' Set properties' . EOL; +$objPHPPresentation->getDocumentProperties()->setCreator('PHPOffice') + ->setLastModifiedBy('PHPPresentation Team') + ->setTitle('Sample 01 Title') + ->setSubject('Sample 01 Subject') + ->setDescription('Sample 01 Description') + ->setKeywords('office 2007 openxml libreoffice odt php') + ->setCategory('Sample Category'); // Create slide -echo date('H:i:s') . ' Create slide'.EOL; +echo date('H:i:s') . ' Create slide' . EOL; $currentSlide = $objPHPPresentation->getActiveSlide(); // Create a shape (drawing) -echo date('H:i:s') . ' Create a shape (drawing)'.EOL; +echo date('H:i:s') . ' Create a shape (drawing)' . EOL; $shape = $currentSlide->createDrawingShape(); $shape->setName('PHPPresentation logo') - ->setDescription('PHPPresentation logo') - ->setPath('./resources/phppowerpoint_logo.gif') - ->setHeight(36) - ->setOffsetX(10) - ->setOffsetY(10); + ->setDescription('PHPPresentation logo') + ->setPath('./resources/phppowerpoint_logo.gif') + ->setHeight(36) + ->setOffsetX(10) + ->setOffsetY(10); $shape->getShadow()->setVisible(true) - ->setDirection(45) - ->setDistance(10); + ->setDirection(45) + ->setDistance(10); $shape->getHyperlink()->setUrl('https://github.com/PHPOffice/PHPPresentation/')->setTooltip('PHPPresentation'); // Create a shape (text) -echo date('H:i:s') . ' Create a shape (rich text)'.EOL; +echo date('H:i:s') . ' Create a shape (rich text)' . EOL; $shape = $currentSlide->createRichTextShape() - ->setHeight(300) - ->setWidth(600) - ->setOffsetX(170) - ->setOffsetY(180); -$shape->getActiveParagraph()->getAlignment()->setHorizontal( Alignment::HORIZONTAL_CENTER ); + ->setHeight(300) + ->setWidth(600) + ->setOffsetX(170) + ->setOffsetY(180); +$shape->getActiveParagraph()->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); $textRun = $shape->createTextRun('Thank you for using PHPPresentation!'); $textRun->getFont()->setBold(true) - ->setSize(60) - ->setColor( new Color( 'FFE06B20' ) ); + ->setSize(60) + ->setColor(new Color('FFE06B20')); // Save file echo write($objPHPPresentation, basename(__FILE__, '.php'), $writers); if (!CLI) { - include_once 'Sample_Footer.php'; + include_once 'Sample_Footer.php'; } diff --git a/samples/Sample_02_Serialized.php b/samples/Sample_02_Serialized.php index 1d9b3b60e..5a5159f86 100644 --- a/samples/Sample_02_Serialized.php +++ b/samples/Sample_02_Serialized.php @@ -13,7 +13,7 @@ // Set properties echo date('H:i:s') . ' Set properties'.EOL; -$objPHPPresentation->getProperties()->setCreator('PHPOffice') +$objPHPPresentation->getDocumentProperties()->setCreator('PHPOffice') ->setLastModifiedBy('PHPPresentation Team') ->setTitle('Sample 03 Title') ->setSubject('Sample 03 Subject') diff --git a/samples/Sample_03_Image.php b/samples/Sample_03_Image.php index 964a25095..ea1ad2afe 100644 --- a/samples/Sample_03_Image.php +++ b/samples/Sample_03_Image.php @@ -4,7 +4,7 @@ use PhpOffice\PhpPresentation\PhpPresentation; use PhpOffice\PhpPresentation\Shape\Drawing; -use PhpOffice\PhpPresentation\Shape\MemoryDrawing; +use PhpOffice\PhpPresentation\Shape\Media; // Create new PHPPresentation object echo date('H:i:s') . ' Create new PHPPresentation object'.EOL; @@ -22,25 +22,61 @@ // Add a generated drawing to the slide echo date('H:i:s') . ' Add a drawing to the slide'.EOL; -$shape = new MemoryDrawing(); +$shape = new Drawing\Gd(); $shape->setName('Sample image') ->setDescription('Sample image') ->setImageResource($gdImage) - ->setRenderingFunction(MemoryDrawing::RENDERING_JPEG) - ->setMimeType(MemoryDrawing::MIMETYPE_DEFAULT) + ->setRenderingFunction(Drawing\Gd::RENDERING_JPEG) + ->setMimeType(Drawing\Gd::MIMETYPE_DEFAULT) ->setHeight(36) ->setOffsetX(10) ->setOffsetY(10); $currentSlide->addShape($shape); // Add a file drawing (GIF) to the slide -$shape = new Drawing(); +$shape = new Drawing\File(); $shape->setName('PHPPresentation logo') - ->setDescription('PHPPresentation logo') - ->setPath('./resources/phppowerpoint_logo.gif') - ->setHeight(36) - ->setOffsetX(10) - ->setOffsetY(100); + ->setDescription('PHPPresentation logo') + ->setPath('./resources/phppowerpoint_logo.gif') + ->setHeight(36) + ->setOffsetX(10) + ->setOffsetY(100); +$currentSlide->addShape($shape); + +// Add a file drawing (Zip) to the slide +$shape = new Drawing\ZipFile(); +$shape->setName('PHPPresentation logo') + ->setDescription('PHPPresentation logo') + ->setPath('zip://./resources/Sample_12.pptx#ppt/media/phppowerpoint_logo1.gif') + ->setResizeProportional(false) + ->setHeight(36) + ->setWidth(36) + ->setOffsetX(10) + ->setOffsetY(150); +$currentSlide->addShape($shape); + +// Add a file drawing (JPEG) to the slide +$shape = new Drawing\Base64(); +$shape->setName('PHPPresentation logo') + ->setDescription('PHPPresentation logo') + ->setData('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIAtAFAAMBIgACEQEDEQH/xAAcAAEAAwEBAQEBAAAAAAAAAAAABAUGAwcCAQj/xABdEAABAwICAwYQCgcGBQIFBQEAAQIDBAUGERIhMQcTFkFR0RQVIjU2VFVhcXORkpOxssEXMjRSU3KBlKHSIzM3QlZ0giRioqPC4aSz0+LwQ2QlY2XD8SZFR4OERv/EABsBAQACAwEBAAAAAAAAAAAAAAADBQIEBgEH/8QAQREAAQMCBAMDCgUDAwQDAQEAAAECAwQREiExUQUTQRRhcSIyM1KBkaGxwfAVIzRC0TZy4QZT8RYkkqIlQ2KCNf/aAAwDAQACEQMRAD8A+eklo7l0f3dnMOklo7l0f3dnMTQddy2bIVeJ25C6SWjuXR/d2cw6SWjuXR/d2cxNA5bNkPMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7t5iaBy2bIMTtzhT0VJSaXQtLDBp5aW9xo3PLlyO4BkiImh5cAAyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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', false) + ->setResizeProportional(false) + ->setHeight(36) + ->setWidth(36) + ->setOffsetX(10) + ->setOffsetY(200); +$currentSlide->addShape($shape); + +// Add a video to the slide +$shape = new Media(); +$shape->setName('Video') + ->setDescription('Video') + ->setPath(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? './resources/sintel_trailer-480p.wmv' : './resources/sintel_trailer-480p.ogv') + ->setResizeProportional(false) + ->setHeight(90) + ->setWidth(90) + ->setOffsetX(10) + ->setOffsetY(300); $currentSlide->addShape($shape); // Save file diff --git a/samples/Sample_04_Table.php b/samples/Sample_04_Table.php index 4aa2fa2e5..4c2f3b451 100644 --- a/samples/Sample_04_Table.php +++ b/samples/Sample_04_Table.php @@ -13,7 +13,7 @@ // Set properties echo date('H:i:s') . ' Set properties'.EOL; -$objPHPPresentation->getProperties()->setCreator('PHPOffice') +$objPHPPresentation->getDocumentProperties()->setCreator('PHPOffice') ->setLastModifiedBy('PHPPresentation Team') ->setTitle('Sample 06 Title') ->setSubject('Sample 06 Subject') diff --git a/samples/Sample_05_Chart.php b/samples/Sample_05_Chart.php index cb3547b11..75bbca530 100644 --- a/samples/Sample_05_Chart.php +++ b/samples/Sample_05_Chart.php @@ -45,6 +45,7 @@ function fnSlide_Area(PhpPresentation $objPHPPresentation) { $series->setShowSeriesName(true); $series->setShowValue(true); $series->getFill()->setStartColor(new Color('FF93A9CE')); + $series->setLabelPosition(Series::LABEL_INSIDEEND); $areaChart->addSeries($series); // Create a shape (chart) @@ -108,6 +109,7 @@ function fnSlide_Bar(PhpPresentation $objPHPPresentation) { $shape->getTitle()->getFont()->setItalic(true); $shape->getTitle()->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT); $shape->getPlotArea()->getAxisX()->setTitle('Month'); + $shape->getPlotArea()->getAxisY()->getFont()->getColor()->setRGB('00FF00'); $shape->getPlotArea()->getAxisY()->setTitle('Downloads'); $shape->getPlotArea()->setType($barChart); $shape->getLegend()->getBorder()->setLineStyle(Border::LINE_SINGLE); @@ -209,6 +211,7 @@ function fnSlide_BarStacked(PhpPresentation $objPHPPresentation) { $shape->getLegend()->getBorder()->setLineStyle( Border::LINE_SINGLE ); $shape->getLegend()->getFont()->setItalic( true ); } + function fnSlide_BarPercentStacked(PhpPresentation $objPHPPresentation) { global $oFill; global $oShadow; @@ -492,17 +495,19 @@ function fnSlide_Scatter(PhpPresentation $objPHPPresentation) { $lineChart = new Scatter(); $series = new Series('Downloads', $seriesData); $series->setShowSeriesName(true); + $series->getMarker()->setSymbol(\PhpOffice\PhpPresentation\Shape\Chart\Marker::SYMBOL_DASH); + $series->getMarker()->setSize(10); $lineChart->addSeries($series); // Create a shape (chart) echo date('H:i:s') . ' Create a shape (chart)'.EOL; $shape = $currentSlide->createChartShape(); $shape->setName('PHPPresentation Daily Download Distribution') - ->setResizeProportional(false) - ->setHeight(550) - ->setWidth(700) - ->setOffsetX(120) - ->setOffsetY(80); + ->setResizeProportional(false) + ->setHeight(550) + ->setWidth(700) + ->setOffsetX(120) + ->setOffsetY(80); $shape->setShadow($oShadow); $shape->setFill($oFill); $shape->getBorder()->setLineStyle(Border::LINE_SINGLE); @@ -521,7 +526,7 @@ function fnSlide_Scatter(PhpPresentation $objPHPPresentation) { // Set properties echo date('H:i:s') . ' Set properties'.EOL; -$objPHPPresentation->getProperties()->setCreator('PHPOffice') +$objPHPPresentation->getDocumentProperties()->setCreator('PHPOffice') ->setLastModifiedBy('PHPPresentation Team') ->setTitle('Sample 07 Title') ->setSubject('Sample 07 Subject') diff --git a/samples/Sample_05_Chart_Line.php b/samples/Sample_05_Chart_Line.php index e249d69c2..bac621988 100644 --- a/samples/Sample_05_Chart_Line.php +++ b/samples/Sample_05_Chart_Line.php @@ -20,7 +20,7 @@ // Set properties echo date('H:i:s') . ' Set properties' . EOL; -$objPHPPresentation->getProperties()->setCreator('PHPOffice')->setLastModifiedBy('PHPPresentation Team')->setTitle('Sample 07 Title')->setSubject('Sample 07 Subject')->setDescription('Sample 07 Description')->setKeywords('office 2007 openxml libreoffice odt php')->setCategory('Sample Category'); +$objPHPPresentation->getDocumentProperties()->setCreator('PHPOffice')->setLastModifiedBy('PHPPresentation Team')->setTitle('Sample 07 Title')->setSubject('Sample 07 Subject')->setDescription('Sample 07 Description')->setKeywords('office 2007 openxml libreoffice odt php')->setCategory('Sample Category'); // Remove first slide echo date('H:i:s') . ' Remove first slide' . EOL; @@ -36,13 +36,13 @@ // Generate sample data for chart echo date('H:i:s') . ' Generate sample data for chart' . EOL; $seriesData = array( - 'Monday' => 12, - 'Tuesday' => 15, - 'Wednesday' => 13, - 'Thursday' => 17, - 'Friday' => 14, - 'Saturday' => 9, - 'Sunday' => 7 + 'Monday' => 12, + 'Tuesday' => 15, + 'Wednesday' => 13, + 'Thursday' => 17, + 'Friday' => 14, + 'Saturday' => 9, + 'Sunday' => 7 ); // Create templated slide @@ -77,8 +77,18 @@ $currentSlide = createTemplatedSlide($objPHPPresentation); // Create a line chart (that should be inserted in a shape) +$oOutline = new \PhpOffice\PhpPresentation\Style\Outline(); +$oOutline->getFill()->setFillType(Fill::FILL_SOLID); +$oOutline->getFill()->setStartColor(new Color(Color::COLOR_YELLOW)); +$oOutline->setWidth(2); + echo date('H:i:s') . ' Create a line chart (that should be inserted in a chart shape)' . EOL; $lineChart1 = clone $lineChart; +$series1 = $lineChart1->getSeries(); +$series1[0]->setOutline($oOutline); +$series1[0]->getMarker()->setSymbol(\PhpOffice\PhpPresentation\Shape\Chart\Marker::SYMBOL_DIAMOND); +$series1[0]->getMarker()->setSize(7); +$lineChart1->setSeries($series1); // Create a shape (chart) echo date('H:i:s') . ' Create a shape (chart)' . EOL; @@ -98,9 +108,11 @@ // Create a line chart (that should be inserted in a shape) echo date('H:i:s') . ' Create a line chart (that should be inserted in a chart shape)' . EOL; $lineChart2 = clone $lineChart; -$series2 = $lineChart2->getData(); -$series2[0]->getFill()->setFillType(Fill::FILL_SOLID); -$lineChart2->setData($series2); +$series2 = $lineChart2->getSeries(); +$series2[0]->getFont()->setSize(25); +$series2[0]->getMarker()->setSymbol(\PhpOffice\PhpPresentation\Shape\Chart\Marker::SYMBOL_TRIANGLE); +$series2[0]->getMarker()->setSize(10); +$lineChart2->setSeries($series2); // Create a shape (chart) echo date('H:i:s') . ' Create a shape (chart)' . EOL; @@ -113,9 +125,36 @@ $shape2->getPlotArea()->getAxisY()->setFormatCode('#,##0'); $currentSlide->addShape($shape2); + +// Create templated slide +echo EOL . date('H:i:s') . ' Create templated slide' . EOL; +$currentSlide = createTemplatedSlide($objPHPPresentation); + +// Create a line chart (that should be inserted in a shape) +echo date('H:i:s') . ' Create a line chart (that should be inserted in a chart shape)' . EOL; +$lineChart3 = clone $lineChart; + +$oGridLines1 = new \PhpOffice\PhpPresentation\Shape\Chart\Gridlines(); +$oGridLines1->getOutline()->setWidth(10); +$oGridLines1->getOutline()->getFill()->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_BLUE)); + +$oGridLines2 = new \PhpOffice\PhpPresentation\Shape\Chart\Gridlines(); +$oGridLines2->getOutline()->setWidth(1); +$oGridLines2->getOutline()->getFill()->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKGREEN)); + +// Create a shape (chart) +echo date('H:i:s') . ' Create a shape (chart3)' . EOL; +echo date('H:i:s') . ' Feature : Gridlines' . EOL; +$shape3 = clone $shape; +$shape3->setName('Shape 3'); +$shape3->getTitle()->setText('Chart with Gridlines'); +$shape3->getPlotArea()->setType($lineChart3); +$shape3->getPlotArea()->getAxisX()->setMajorGridlines($oGridLines1); +$shape3->getPlotArea()->getAxisY()->setMinorGridlines($oGridLines2); +$currentSlide->addShape($shape3); // Save file echo EOL . write($objPHPPresentation, basename(__FILE__, '.php'), $writers); if (!CLI) { - include_once 'Sample_Footer.php'; + include_once 'Sample_Footer.php'; } diff --git a/samples/Sample_05_Chart_with_PHPExcel.php b/samples/Sample_05_Chart_with_PHPExcel.php index 0968fa26a..e10a4eda4 100644 --- a/samples/Sample_05_Chart_with_PHPExcel.php +++ b/samples/Sample_05_Chart_with_PHPExcel.php @@ -19,7 +19,7 @@ // Set properties echo date('H:i:s') . ' Set properties'.EOL; - $objPHPPresentation->getProperties()->setCreator('PHPOffice') + $objPHPPresentation->getDocumentProperties()->setCreator('PHPOffice') ->setLastModifiedBy('PHPPresentation Team') ->setTitle('Sample 08 Title') ->setSubject('Sample 08 Subject') diff --git a/samples/Sample_06_Fill.php b/samples/Sample_06_Fill.php index 906c6916c..563f00baf 100644 --- a/samples/Sample_06_Fill.php +++ b/samples/Sample_06_Fill.php @@ -13,7 +13,7 @@ // Set properties echo date('H:i:s') . ' Set properties'.EOL; -$objPHPPresentation->getProperties()->setCreator('PHPOffice') +$objPHPPresentation->getDocumentProperties()->setCreator('PHPOffice') ->setLastModifiedBy('PHPPresentation Team') ->setTitle('Sample 01 Title') ->setSubject('Sample 01 Subject') diff --git a/samples/Sample_07_Border.php b/samples/Sample_07_Border.php index a6ec67803..a6402fd58 100644 --- a/samples/Sample_07_Border.php +++ b/samples/Sample_07_Border.php @@ -14,7 +14,7 @@ // Set properties echo date('H:i:s') . ' Set properties' . EOL; -$objPHPPresentation->getProperties()->setCreator('PHPOffice')->setLastModifiedBy('PHPPresentation Team')->setTitle('Sample 01 Title')->setSubject('Sample 01 Subject')->setDescription('Sample 01 Description')->setKeywords('office 2007 openxml libreoffice odt php')->setCategory('Sample Category'); +$objPHPPresentation->getDocumentProperties()->setCreator('PHPOffice')->setLastModifiedBy('PHPPresentation Team')->setTitle('Sample 01 Title')->setSubject('Sample 01 Subject')->setDescription('Sample 01 Description')->setKeywords('office 2007 openxml libreoffice odt php')->setCategory('Sample Category'); // Create slide echo date('H:i:s') . ' Create slide' . EOL; diff --git a/samples/Sample_08_Group.php b/samples/Sample_08_Group.php index 3f4e6e80d..55b35f4c3 100644 --- a/samples/Sample_08_Group.php +++ b/samples/Sample_08_Group.php @@ -12,7 +12,7 @@ // Set properties echo date('H:i:s') . ' Set properties'.EOL; -$objPHPPresentation->getProperties()->setCreator('PHPOffice') +$objPHPPresentation->getDocumentProperties()->setCreator('PHPOffice') ->setLastModifiedBy('PHPPresentation Team') ->setTitle('Sample 01 Title') ->setSubject('Sample 01 Subject') diff --git a/samples/Sample_09_SlideNote.php b/samples/Sample_09_SlideNote.php index b17537f43..0462dc0c0 100644 --- a/samples/Sample_09_SlideNote.php +++ b/samples/Sample_09_SlideNote.php @@ -11,46 +11,51 @@ // Create new PHPPresentation object echo date('H:i:s') . ' Create new PHPPresentation object' . EOL; $objPHPPresentation = new PhpPresentation(); +$oLayout = $objPHPPresentation->getLayout(); // Set properties echo date('H:i:s') . ' Set properties' . EOL; -$objPHPPresentation->getProperties()->setCreator('PHPOffice')->setLastModifiedBy('PHPPresentation Team')->setTitle('Sample 01 Title')->setSubject('Sample 01 Subject')->setDescription('Sample 01 Description')->setKeywords('office 2007 openxml libreoffice odt php')->setCategory('Sample Category'); +$objPHPPresentation->getDocumentProperties()->setCreator('PHPOffice')->setLastModifiedBy('PHPPresentation Team')->setTitle('Sample 01 Title')->setSubject('Sample 01 Subject')->setDescription('Sample 01 Description')->setKeywords('office 2007 openxml libreoffice odt php')->setCategory('Sample Category'); // Create slide echo date('H:i:s') . ' Create slide' . EOL; $currentSlide = $objPHPPresentation->getActiveSlide(); // Create a shape (drawing) -echo date('H:i:s') . ' Create a shape (drawing)'.EOL; +echo date('H:i:s') . ' Create a shape (drawing)' . EOL; $shape = $currentSlide->createDrawingShape(); $shape->setName('PHPPresentation logo') -->setDescription('PHPPresentation logo') -->setPath('./resources/phppowerpoint_logo.gif') -->setHeight(36) -->setOffsetX(10) -->setOffsetY(10); + ->setDescription('PHPPresentation logo') + ->setPath('./resources/phppowerpoint_logo.gif') + ->setHeight(36) + ->setOffsetX(10) + ->setOffsetY(10); $shape->getShadow()->setVisible(true) -->setDirection(45) -->setDistance(10); + ->setDirection(45) + ->setDistance(10); $shape->getHyperlink()->setUrl('https://github.com/PHPOffice/PHPPresentation/')->setTooltip('PHPPresentation'); // Create a shape (text) -echo date('H:i:s') . ' Create a shape (rich text)'.EOL; +echo date('H:i:s') . ' Create a shape (rich text)' . EOL; $shape = $currentSlide->createRichTextShape() ->setHeight(300) ->setWidth(600) ->setOffsetX(170) ->setOffsetY(180); -$shape->getActiveParagraph()->getAlignment()->setHorizontal( Alignment::HORIZONTAL_CENTER ); +$shape->getActiveParagraph()->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); $textRun = $shape->createTextRun('Thank you for using PHPPresentation!'); $textRun->getFont()->setBold(true) -->setSize(60) -->setColor( new Color( 'FFE06B20' ) ); + ->setSize(60) + ->setColor(new Color('FFE06B20')); // Set Note echo date('H:i:s') . ' Set Note' . EOL; $oNote = $currentSlide->getNote(); -$oRichText = $oNote->createRichTextShape()->setHeight(300)->setWidth(600)->setOffsetX(170)->setOffsetY(180);; +$oRichText = $oNote->createRichTextShape() + ->setHeight($oLayout->getCY($oLayout::UNIT_PIXEL)) + ->setWidth($oLayout->getCX($oLayout::UNIT_PIXEL)) + ->setOffsetX(170) + ->setOffsetY(180); $oRichText->createTextRun('A class library'); $oRichText->createParagraph()->createTextRun('Written in PHP'); $oRichText->createParagraph()->createTextRun('Representing a presentation'); @@ -59,5 +64,5 @@ // Save file echo write($objPHPPresentation, basename(__FILE__, '.php'), $writers); if (!CLI) { - include_once 'Sample_Footer.php'; + include_once 'Sample_Footer.php'; } diff --git a/samples/Sample_10_Transition.php b/samples/Sample_10_Transition.php index b9791406b..9a760fc97 100644 --- a/samples/Sample_10_Transition.php +++ b/samples/Sample_10_Transition.php @@ -13,7 +13,7 @@ // Set properties echo date('H:i:s') . ' Set properties'.EOL; -$objPHPPresentation->getProperties()->setCreator('PHPOffice') +$objPHPPresentation->getDocumentProperties()->setCreator('PHPOffice') ->setLastModifiedBy('PHPPresentation Team') ->setTitle('Sample 10 Title') ->setSubject('Sample 10 Subject') diff --git a/samples/Sample_11_Shape.php b/samples/Sample_11_Shape.php index b6ab9f58c..37dc5a221 100644 --- a/samples/Sample_11_Shape.php +++ b/samples/Sample_11_Shape.php @@ -7,76 +7,147 @@ include_once 'Sample_Header.php'; -function fnSlideRichText(PhpPresentation $objPHPPresentation) { - // Create templated slide - echo date('H:i:s') . ' Create templated slide'.EOL; - $currentSlide = createTemplatedSlide($objPHPPresentation); - - // Create a shape (text) - echo date('H:i:s') . ' Create a shape (rich text)'.EOL; - $shape = $currentSlide->createRichTextShape(); - $shape->setHeight(100); - $shape->setWidth(600); - $shape->setOffsetX(100); - $shape->setOffsetY(100); - $shape->getActiveParagraph()->getAlignment()->setHorizontal( Alignment::HORIZONTAL_LEFT ); - - $textRun = $shape->createTextRun('RichText with'); - $textRun->getFont()->setBold(true); - $textRun->getFont()->setSize(28); - $textRun->getFont()->setColor(new Color( 'FF000000' )); - - $shape->createBreak(); - - $textRun = $shape->createTextRun('Multiline'); - $textRun->getFont()->setBold(true); - $textRun->getFont()->setSize(60); - $textRun->getFont()->setColor(new Color( 'FF000000' )); +function fnSlideRichText(PhpPresentation $objPHPPresentation) +{ + // Create templated slide + echo date('H:i:s') . ' Create templated slide' . EOL; + $currentSlide = createTemplatedSlide($objPHPPresentation); + + // Create a shape (text) + echo date('H:i:s') . ' Create a shape (rich text)' . EOL; + $shape = $currentSlide->createRichTextShape(); + $shape->setHeight(100); + $shape->setWidth(600); + $shape->setOffsetX(100); + $shape->setOffsetY(100); + $shape->getActiveParagraph()->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT); + + $textRun = $shape->createTextRun('RichText with'); + $textRun->getFont()->setBold(true); + $textRun->getFont()->setSize(28); + $textRun->getFont()->setColor(new Color('FF000000')); + + $shape->createBreak(); + + $textRun = $shape->createTextRun('Multiline'); + $textRun->getFont()->setBold(true); + $textRun->getFont()->setSize(60); + $textRun->getFont()->setColor(new Color('FF000000')); +} + +function fnSlideRichTextLineSpacing(PhpPresentation $objPHPPresentation) +{ + // Create templated slide + echo date('H:i:s') . ' Create templated slide' . EOL; + $currentSlide = createTemplatedSlide($objPHPPresentation); + + // Create a shape (text) + echo date('H:i:s') . ' Create a shape (rich text) with line spacing (100)' . EOL; + $shape = $currentSlide->createRichTextShape(); + $shape->setHeight(100); + $shape->setWidth(400); + $shape->setOffsetX(100); + $shape->setOffsetY(100); + $shape->getActiveParagraph()->setLineSpacing(100); + + $shape->createTextRun('RichText with'); + $shape->createBreak(); + $shape->createTextRun('Line Spacing 100'); + + // Create a shape (text) + echo date('H:i:s') . ' Create a shape (rich text) with line spacing (200)' . EOL; + $shape = $currentSlide->createRichTextShape(); + $shape->setHeight(100); + $shape->setWidth(400); + $shape->setOffsetX(100); + $shape->setOffsetY(200); + $shape->getActiveParagraph()->setLineSpacing(200); + + $shape->createTextRun('RichText with'); + $shape->createBreak(); + $shape->createTextRun('Line Spacing 200'); + + // Create a shape (text) + echo date('H:i:s') . ' Create a shape (rich text) with line spacing (300)' . EOL; + $shape = $currentSlide->createRichTextShape(); + $shape->setHeight(100); + $shape->setWidth(400); + $shape->setOffsetX(100); + $shape->setOffsetY(300); + $shape->getActiveParagraph()->setLineSpacing(300); + + $shape->createTextRun('RichText with'); + $shape->createBreak(); + $shape->createTextRun('Line Spacing 300'); } -function fnSlideRichTextShadow(PhpPresentation $objPHPPresentation) { +function fnSlideRichTextShadow(PhpPresentation $objPHPPresentation) +{ // Create templated slide - echo date('H:i:s') . ' Create templated slide'.EOL; + echo date('H:i:s') . ' Create templated slide' . EOL; $currentSlide = createTemplatedSlide($objPHPPresentation); // Create a shape (text) - echo date('H:i:s') . ' Create a shape (rich text) with shadow'.EOL; + echo date('H:i:s') . ' Create a shape (rich text) with shadow' . EOL; $shape = $currentSlide->createRichTextShape(); - $shape->setHeight(100); - $shape->setWidth(400); - $shape->setOffsetX(100); - $shape->setOffsetY(100); - $shape->getActiveParagraph()->getAlignment()->setHorizontal( Alignment::HORIZONTAL_LEFT ); + $shape->setHeight(100); + $shape->setWidth(400); + $shape->setOffsetX(100); + $shape->setOffsetY(100); + $shape->getActiveParagraph()->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT); $shape->getShadow()->setVisible(true)->setAlpha(75)->setBlurRadius(2)->setDirection(45); $textRun = $shape->createTextRun('RichText with shadow'); - $textRun->getFont()->setColor(new Color( 'FF000000' )); + $textRun->getFont()->setColor(new Color('FF000000')); +} + +function fnSlideRichTextList(PhpPresentation $objPHPPresentation) +{ + // Create templated slide + echo date('H:i:s') . ' Create templated slide' . EOL; + $currentSlide = createTemplatedSlide($objPHPPresentation); + + // Create a shape (text) + echo date('H:i:s') . ' Create a shape (rich text) with list with red bullet' . EOL; + $shape = $currentSlide->createRichTextShape(); + $shape->setHeight(100); + $shape->setWidth(400); + $shape->setOffsetX(100); + $shape->setOffsetY(100); + $shape->getActiveParagraph()->getBulletStyle()->setBulletType(Bullet::TYPE_BULLET)->setBulletColor(new Color(Color::COLOR_RED)); + + $shape->createTextRun('Alpha'); + $shape->createParagraph()->createTextRun('Beta'); + $shape->createParagraph()->createTextRun('Delta'); + $shape->createParagraph()->createTextRun('Epsilon'); } // Create new PHPPresentation object -echo date('H:i:s') . ' Create new PHPPresentation object'.EOL; +echo date('H:i:s') . ' Create new PHPPresentation object' . EOL; $objPHPPresentation = new PhpPresentation(); // Set properties -echo date('H:i:s') . ' Set properties'.EOL; -$oProperties = $objPHPPresentation->getProperties(); +echo date('H:i:s') . ' Set properties' . EOL; +$oProperties = $objPHPPresentation->getDocumentProperties(); $oProperties->setCreator('PHPOffice') - ->setLastModifiedBy('PHPPresentation Team') - ->setTitle('Sample 11 Title') - ->setSubject('Sample 11 Subject') - ->setDescription('Sample 11 Description') - ->setKeywords('office 2007 openxml libreoffice odt php') - ->setCategory('Sample Category'); + ->setLastModifiedBy('PHPPresentation Team') + ->setTitle('Sample 11 Title') + ->setSubject('Sample 11 Subject') + ->setDescription('Sample 11 Description') + ->setKeywords('office 2007 openxml libreoffice odt php') + ->setCategory('Sample Category'); // Remove first slide -echo date('H:i:s') . ' Remove first slide'.EOL; +echo date('H:i:s') . ' Remove first slide' . EOL; $objPHPPresentation->removeSlideByIndex(0); fnSlideRichText($objPHPPresentation); +fnSlideRichTextLineSpacing($objPHPPresentation); fnSlideRichTextShadow($objPHPPresentation); +fnSlideRichTextList($objPHPPresentation); // Save file echo write($objPHPPresentation, basename(__FILE__, '.php'), $writers); if (!CLI) { - include_once 'Sample_Footer.php'; + include_once 'Sample_Footer.php'; } diff --git a/samples/Sample_12_Reader_ODPresentation.php b/samples/Sample_12_Reader_ODPresentation.php index e66502187..acb582956 100644 --- a/samples/Sample_12_Reader_ODPresentation.php +++ b/samples/Sample_12_Reader_ODPresentation.php @@ -4,16 +4,9 @@ include_once 'Sample_Header.php'; -use PhpOffice\PhpPresentation\PhpPresentation; -use PhpOffice\PhpPresentation\DocumentLayout; use PhpOffice\PhpPresentation\IOFactory; -use PhpOffice\PhpPresentation\Style\Alignment; -use PhpOffice\PhpPresentation\Style\Color; use PhpOffice\PhpPresentation\Slide; -use PhpOffice\PhpPresentation\AbstractShape; -use PhpOffice\PhpPresentation\Shape\MemoryDrawing; use PhpOffice\PhpPresentation\Shape\RichText; -use PhpOffice\PhpPresentation\Shape\Group; $pptReader = IOFactory::createReader('ODPresentation'); $oPHPPresentation = $pptReader->load('resources/Sample_12.odp'); diff --git a/samples/Sample_12_Reader_PowerPoint2007.php b/samples/Sample_12_Reader_PowerPoint2007.php index 074f0fad5..009361547 100644 --- a/samples/Sample_12_Reader_PowerPoint2007.php +++ b/samples/Sample_12_Reader_PowerPoint2007.php @@ -4,16 +4,9 @@ include_once 'Sample_Header.php'; -use PhpOffice\PhpPresentation\PhpPresentation; -use PhpOffice\PhpPresentation\DocumentLayout; use PhpOffice\PhpPresentation\IOFactory; -use PhpOffice\PhpPresentation\Style\Alignment; -use PhpOffice\PhpPresentation\Style\Color; use PhpOffice\PhpPresentation\Slide; -use PhpOffice\PhpPresentation\AbstractShape; -use PhpOffice\PhpPresentation\Shape\MemoryDrawing; use PhpOffice\PhpPresentation\Shape\RichText; -use PhpOffice\PhpPresentation\Shape\Group; $pptReader = IOFactory::createReader('PowerPoint2007'); $oPHPPresentation = $pptReader->load('resources/Sample_12.pptx'); diff --git a/samples/Sample_12_Reader_PowerPoint97.php b/samples/Sample_12_Reader_PowerPoint97.php index 9db9f458c..bcc780ce7 100644 --- a/samples/Sample_12_Reader_PowerPoint97.php +++ b/samples/Sample_12_Reader_PowerPoint97.php @@ -4,18 +4,11 @@ include_once 'Sample_Header.php'; -use PhpOffice\PhpPresentation\PhpPresentation; -use PhpOffice\PhpPresentation\DocumentLayout; use PhpOffice\PhpPresentation\IOFactory; -use PhpOffice\PhpPresentation\Style\Alignment; -use PhpOffice\PhpPresentation\Style\Color; use PhpOffice\PhpPresentation\Slide; -use PhpOffice\PhpPresentation\AbstractShape; -use PhpOffice\PhpPresentation\Shape\MemoryDrawing; use PhpOffice\PhpPresentation\Shape\RichText; -use PhpOffice\PhpPresentation\Shape\Group; -$pptReader = PhpOffice\PhpPresentation\IOFactory::createReader('PowerPoint97'); +$pptReader = IOFactory::createReader('PowerPoint97'); $oPHPPresentation = $pptReader->load('resources/Sample_12.ppt'); $oTree = new PhpPptTree($oPHPPresentation); diff --git a/samples/Sample_14_Zoom.php b/samples/Sample_14_Zoom.php index 253b6dc76..9b7691779 100644 --- a/samples/Sample_14_Zoom.php +++ b/samples/Sample_14_Zoom.php @@ -11,7 +11,7 @@ $objPHPPresentation = new PhpPresentation(); // Set the zoom to 200% -$objPHPPresentation->setZoom(3); +$objPHPPresentation->getPresentationProperties()->setZoom(3); // Create slide echo date('H:i:s') . ' Create slide'.EOL; diff --git a/samples/Sample_16_Thumbnail.php b/samples/Sample_16_Thumbnail.php new file mode 100644 index 000000000..15c33fbe8 --- /dev/null +++ b/samples/Sample_16_Thumbnail.php @@ -0,0 +1,27 @@ +getPresentationProperties()->setThumbnailPath(__DIR__.'\resources\phppowerpoint_logo.gif'); + +// Create slide +echo date('H:i:s') . ' Create slide'.EOL; +$oSlide1 = $objPHPPresentation->getActiveSlide(); +$oSlide1->addShape(clone $oShapeDrawing); +$oSlide1->addShape(clone $oShapeRichText); + +// Save file +echo write($objPHPPresentation, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; +} diff --git a/samples/Sample_17_Comment.php b/samples/Sample_17_Comment.php new file mode 100644 index 000000000..727d72bc6 --- /dev/null +++ b/samples/Sample_17_Comment.php @@ -0,0 +1,48 @@ +getActiveSlide(); +$oSlide1->addShape(clone $oShapeDrawing); +$oSlide1->addShape(clone $oShapeRichText); + +$oAuthor = new \PhpOffice\PhpPresentation\Shape\Comment\Author(); +$oAuthor->setName('Progi1984'); +$oAuthor->setInitials('P'); + +// Add Comment 1 +echo date('H:i:s') . ' Add Comment 1'.EOL; +$oComment1 = new \PhpOffice\PhpPresentation\Shape\Comment(); +$oComment1->setText('Text A'); +$oComment1->setOffsetX(10); +$oComment1->setOffsetY(55); +$oComment1->setDate(time()); +$oComment1->setAuthor($oAuthor); +$oSlide1->addShape($oComment1); + +// Add Comment +echo date('H:i:s') . ' Add Comment 2'.EOL; +$oComment2 = new \PhpOffice\PhpPresentation\Shape\Comment(); +$oComment2->setText('Text B'); +$oComment2->setOffsetX(170); +$oComment2->setOffsetY(180); +$oComment2->setDate(time()); +$oComment2->setAuthor($oAuthor); +$oSlide1->addShape($oComment2); + +// Save file +echo write($objPHPPresentation, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; +} diff --git a/samples/Sample_18_Animation.php b/samples/Sample_18_Animation.php new file mode 100644 index 000000000..9f46eca86 --- /dev/null +++ b/samples/Sample_18_Animation.php @@ -0,0 +1,60 @@ +getActiveSlide(); +$oSlide1->addShape($oDrawing1); +$oSlide1->addShape($oRichText1); + +$oAnimation1 = new Animation(); +$oAnimation1->addShape($oDrawing1); +$oSlide1->addAnimation($oAnimation1); + +$oAnimation2 = new Animation(); +$oAnimation2->addShape($oRichText1); +$oSlide1->addAnimation($oAnimation2); + +$oDrawing2 = clone $oShapeDrawing; +$oRichText2 = clone $oShapeRichText; + +$oSlide2 = $objPHPPresentation->createSlide(); +$oSlide2->addShape($oDrawing2); +$oSlide2->addShape($oRichText2); + +$oAnimation4 = new Animation(); +$oAnimation4->addShape($oRichText2); +$oSlide2->addAnimation($oAnimation4); + +$oAnimation3 = new Animation(); +$oAnimation3->addShape($oDrawing2); +$oSlide2->addAnimation($oAnimation3); + +$oDrawing3 = clone $oShapeDrawing; +$oRichText3 = clone $oShapeRichText; + +$oSlide3 = $objPHPPresentation->createSlide(); +$oSlide3->addShape($oDrawing3); +$oSlide3->addShape($oRichText3); + +$oAnimation5 = new Animation(); +$oAnimation5->addShape($oRichText3); +$oAnimation5->addShape($oDrawing3); +$oSlide3->addAnimation($oAnimation5); + +// Save file +echo write($objPHPPresentation, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; +} diff --git a/samples/Sample_19_SlideMaster.php b/samples/Sample_19_SlideMaster.php new file mode 100644 index 000000000..3fe68add2 --- /dev/null +++ b/samples/Sample_19_SlideMaster.php @@ -0,0 +1,111 @@ +getDocumentProperties()->setCreator('PHPOffice') + ->setLastModifiedBy('PHPPresentation Team') + ->setTitle('Sample 19 SlideMaster') + ->setSubject('Sample 19 Subject') + ->setDescription('Sample 19 Description') + ->setKeywords('office 2007 openxml libreoffice odt php') + ->setCategory('Sample Category'); + +// Create slide +echo date('H:i:s') . ' Create slide' . EOL; +$currentSlide = $objPHPPresentation->getActiveSlide(); + +// Create a master layout +echo date('H:i:s') . ' Create masterslide layout' . EOL; +// Some decorative lines +$oMasterSlide = $objPHPPresentation->getAllMasterSlides()[0]; +$shape = $oMasterSlide->createLineShape(0, 670, 960, 670)->getBorder()->setColor(new Color(Color::COLOR_RED))->setLineWidth(2); +$shape = $oMasterSlide->createLineShape(0, 672, 960, 672)->getBorder()->setColor(new Color(Color::COLOR_WHITE))->setLineWidth(2); +$shape = $oMasterSlide->createLineShape(0, 674, 960, 674)->getBorder()->setColor(new Color(Color::COLOR_DARKBLUE))->setLineWidth(2); +// Title placeholder +$shape = $oMasterSlide->createRichTextShape(); +$shape->setWidthAndHeight(960, 80)->setOffsetX(0)->setOffsetY(60); +$shape->getFill()->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_BLUE)); +$shape->getActiveParagraph()->getAlignment() + ->setHorizontal(Alignment::HORIZONTAL_RIGHT)->setMarginLeft(200)->setMarginRight(50) + ->setVertical(Alignment::VERTICAL_CENTER); +$shape->getShadow()->setVisible(true) + ->setDirection(90) + ->setDistance(10); +$shape->setAutoFit(RichText::AUTOFIT_NORMAL); +$textRun = $shape->createTextRun('Titel'); +$textRun->getFont()->setBold(true)->setSize(30)->setColor(new Color(Color::COLOR_WHITE)); +$shape->setPlaceHolder(new Placeholder(Placeholder::PH_TYPE_TITLE)); +// Date placeholder +$shape = $oMasterSlide->createRichTextShape(); +$shape->setWidthAndHeight(140, 38)->setOffsetX(50)->setOffsetY(680); +$shape->getActiveParagraph()->getAlignment() + ->setHorizontal(Alignment::HORIZONTAL_LEFT) + ->setVertical(Alignment::VERTICAL_BASE); +$shape->setAutoFit(RichText::AUTOFIT_NORMAL); +$textRun = $shape->createTextRun('01-02-2000')->getFont()->setSize(18); +$shape->setPlaceHolder(new Placeholder(Placeholder::PH_TYPE_DATETIME))->getPlaceholder()->setIdx(10); +// Footer placeholder +$shape = $oMasterSlide->createRichTextShape(); +$shape->setWidthAndHeight(468, 38)->setOffsetX(246)->setOffsetY(680); +$shape->getActiveParagraph()->getAlignment() + ->setHorizontal(Alignment::HORIZONTAL_CENTER) + ->setVertical(Alignment::VERTICAL_BASE); +$shape->setAutoFit(RichText::AUTOFIT_NORMAL); +$textRun = $shape->createTextRun('Placeholder for Footer')->getFont()->setSize(18); +$shape->setPlaceHolder(new Placeholder(Placeholder::PH_TYPE_FOOTER))->getPlaceholder()->setIdx(11); +// Slidenumber placeholder +$shape = $oMasterSlide->createRichTextShape(); +$shape->setWidthAndHeight(140, 38)->setOffsetX(770)->setOffsetY(680); +$shape->getActiveParagraph()->getAlignment() + ->setHorizontal(Alignment::HORIZONTAL_RIGHT) + ->setVertical(Alignment::VERTICAL_BASE); +$shape->setAutoFit(RichText::AUTOFIT_NORMAL); +$textRun = $shape->createTextRun('')->getFont()->setSize(18); +$shape->setPlaceHolder(new Placeholder(Placeholder::PH_TYPE_SLIDENUM))->getPlaceholder()->setIdx(12); + +// Create a shape (drawing) +echo date('H:i:s') . ' Create a shape (drawing)' . EOL; +$shape = $currentSlide->createDrawingShape(); +$shape->setName('PHPPresentation logo') + ->setDescription('PHPPresentation logo') + ->setPath('./resources/phppowerpoint_logo.gif') + ->setHeight(36) + ->setOffsetX(10) + ->setOffsetY(10); +$shape->getShadow()->setVisible(true) + ->setDirection(45) + ->setDistance(10); +$shape->getHyperlink()->setUrl('https://github.com/PHPOffice/PHPPresentation/')->setTooltip('PHPPresentation'); + +// Create a shape (text) linked to a PlaceHolder +echo date('H:i:s') . ' Create a shape (rich text)' . EOL; +$shape = $currentSlide->createRichTextShape(); +$shape->setWidthAndHeight(960, 80)->setOffsetX(0)->setOffsetY(60); +$shape->getFill()->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_BLUE)); +$shape->getActiveParagraph()->getAlignment() + ->setHorizontal(Alignment::HORIZONTAL_RIGHT)->setMarginLeft(200)->setMarginRight(50) + ->setVertical(Alignment::VERTICAL_CENTER); +$shape->setAutoFit(RichText::AUTOFIT_NORMAL); +$shape->setPlaceHolder(new Placeholder(Placeholder::PH_TYPE_TITLE)); + +echo date('H:i:s') . ' Apply the shape collection of the slide master to the slide layout' . EOL; +$oSlideLayout = $oMasterSlide->getAllSlideLayouts()[0]; +$oSlideLayout->setShapeCollection($oMasterSlide->getShapeCollection()); +$currentSlide->setSlideLayout($oSlideLayout); + +// Save file +echo write($objPHPPresentation, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; +} \ No newline at end of file diff --git a/samples/Sample_Header.php b/samples/Sample_Header.php index 3d42577f0..e8da92857 100644 --- a/samples/Sample_Header.php +++ b/samples/Sample_Header.php @@ -10,7 +10,6 @@ use PhpOffice\PhpPresentation\AbstractShape; use PhpOffice\PhpPresentation\DocumentLayout; use PhpOffice\PhpPresentation\Shape\Drawing; -use PhpOffice\PhpPresentation\Shape\MemoryDrawing; use PhpOffice\PhpPresentation\Shape\RichText; use PhpOffice\PhpPresentation\Shape\RichText\BreakElement; use PhpOffice\PhpPresentation\Shape\RichText\TextElement; @@ -43,7 +42,7 @@ $pageTitle .= 'PHPPresentation'; $pageHeading = IS_INDEX ? '' : "

{$pageHeading}

"; -$oShapeDrawing = new Drawing(); +$oShapeDrawing = new Drawing\File(); $oShapeDrawing->setName('PHPPresentation logo') ->setDescription('PHPPresentation logo') ->setPath('./resources/phppowerpoint_logo.gif') @@ -241,14 +240,18 @@ protected function displayPhpPresentation(PhpPresentation $oPHPPpt) protected function displayShape(AbstractShape $shape) { - if($shape instanceof MemoryDrawing) { - $this->append('
  • Shape "MemoryDrawing"
  • '); - } elseif($shape instanceof Drawing) { - $this->append('
  • Shape "Drawing"
  • '); + if($shape instanceof Drawing\Gd) { + $this->append('
  • Shape "Drawing\Gd"
  • '); + } elseif($shape instanceof Drawing\File) { + $this->append('
  • Shape "Drawing\File"
  • '); + } elseif($shape instanceof Drawing\Base64) { + $this->append('
  • Shape "Drawing\Base64"
  • '); + } elseif($shape instanceof Drawing\Zip) { + $this->append('
  • Shape "Drawing\Zip"
  • '); } elseif($shape instanceof RichText) { $this->append('
  • Shape "RichText"
  • '); } else { - var_export($shape); + var_dump($shape); } } @@ -257,18 +260,19 @@ protected function displayPhpPresentationInfo(PhpPresentation $oPHPPpt) $this->append('
    '); $this->append('
    '); $this->append('
    Number of slides
    '.$oPHPPpt->getSlideCount().'
    '); + $this->append('
    Document Layout Name
    '.(empty($oPHPPpt->getLayout()->getDocumentLayout()) ? 'Custom' : $oPHPPpt->getLayout()->getDocumentLayout()).'
    '); $this->append('
    Document Layout Height
    '.$oPHPPpt->getLayout()->getCY(DocumentLayout::UNIT_MILLIMETER).' mm
    '); $this->append('
    Document Layout Width
    '.$oPHPPpt->getLayout()->getCX(DocumentLayout::UNIT_MILLIMETER).' mm
    '); - $this->append('
    Properties : Category
    '.$oPHPPpt->getProperties()->getCategory().'
    '); - $this->append('
    Properties : Company
    '.$oPHPPpt->getProperties()->getCompany().'
    '); - $this->append('
    Properties : Created
    '.$oPHPPpt->getProperties()->getCreated().'
    '); - $this->append('
    Properties : Creator
    '.$oPHPPpt->getProperties()->getCreator().'
    '); - $this->append('
    Properties : Description
    '.$oPHPPpt->getProperties()->getDescription().'
    '); - $this->append('
    Properties : Keywords
    '.$oPHPPpt->getProperties()->getKeywords().'
    '); - $this->append('
    Properties : Last Modified By
    '.$oPHPPpt->getProperties()->getLastModifiedBy().'
    '); - $this->append('
    Properties : Modified
    '.$oPHPPpt->getProperties()->getModified().'
    '); - $this->append('
    Properties : Subject
    '.$oPHPPpt->getProperties()->getSubject().'
    '); - $this->append('
    Properties : Title
    '.$oPHPPpt->getProperties()->getTitle().'
    '); + $this->append('
    Properties : Category
    '.$oPHPPpt->getDocumentProperties()->getCategory().'
    '); + $this->append('
    Properties : Company
    '.$oPHPPpt->getDocumentProperties()->getCompany().'
    '); + $this->append('
    Properties : Created
    '.$oPHPPpt->getDocumentProperties()->getCreated().'
    '); + $this->append('
    Properties : Creator
    '.$oPHPPpt->getDocumentProperties()->getCreator().'
    '); + $this->append('
    Properties : Description
    '.$oPHPPpt->getDocumentProperties()->getDescription().'
    '); + $this->append('
    Properties : Keywords
    '.$oPHPPpt->getDocumentProperties()->getKeywords().'
    '); + $this->append('
    Properties : Last Modified By
    '.$oPHPPpt->getDocumentProperties()->getLastModifiedBy().'
    '); + $this->append('
    Properties : Modified
    '.$oPHPPpt->getDocumentProperties()->getModified().'
    '); + $this->append('
    Properties : Subject
    '.$oPHPPpt->getDocumentProperties()->getSubject().'
    '); + $this->append('
    Properties : Title
    '.$oPHPPpt->getDocumentProperties()->getTitle().'
    '); $this->append('
    '); $this->append('
    '); @@ -320,7 +324,8 @@ protected function displayShapeInfo(AbstractShape $oShape) $this->append('
    Hyperlink
    '.ucfirst(var_export($oShape->hasHyperlink(), true)).'
    '); $this->append('
    Fill
    @Todo
    '); $this->append('
    Border
    @Todo
    '); - if($oShape instanceof MemoryDrawing) { + $this->append('
    IsPlaceholder
    ' . ($oShape->isPlaceholder() ? 'true' : 'false') . '
    '); + if($oShape instanceof Drawing\Gd) { $this->append('
    Name
    '.$oShape->getName().'
    '); $this->append('
    Description
    '.$oShape->getDescription().'
    '); ob_start(); @@ -345,7 +350,10 @@ protected function displayShapeInfo(AbstractShape $oShape) $this->append('
    Alignment Indent
    '.$oParagraph->getAlignment()->getIndent().' px
    '); $this->append('
    Alignment Level
    '.$oParagraph->getAlignment()->getLevel().'
    '); $this->append('
    Bullet Style
    Bullet::'.$this->getConstantName('\PhpOffice\PhpPresentation\Style\Bullet', $oParagraph->getBulletStyle()->getBulletType()).'
    '); - $this->append('
    Bullet Font
    '.$oParagraph->getBulletStyle()->getBulletFont().'
    '); + if ($oParagraph->getBulletStyle()->getBulletType() != Bullet::TYPE_NONE) { + $this->append('
    Bullet Font
    ' . $oParagraph->getBulletStyle()->getBulletFont() . '
    '); + $this->append('
    Bullet Color
    ' . $oParagraph->getBulletStyle()->getBulletColor()->getARGB() . '
    '); + } if ($oParagraph->getBulletStyle()->getBulletType() == Bullet::TYPE_BULLET) { $this->append('
    Bullet Char
    '.$oParagraph->getBulletStyle()->getBulletChar().'
    '); } @@ -353,6 +361,7 @@ protected function displayShapeInfo(AbstractShape $oShape) $this->append('
    Bullet Start At
    '.$oParagraph->getBulletStyle()->getBulletNumericStartAt().'
    '); $this->append('
    Bullet Style
    '.$oParagraph->getBulletStyle()->getBulletNumericStyle().'
    '); } + $this->append('
    Line Spacing
    '.$oParagraph->getLineSpacing().'
    '); $this->append('
    RichText
    '); foreach ($oParagraph->getRichTextElements() as $oRichText) { if($oRichText instanceof BreakElement) { diff --git a/samples/resources/SamplePassword.pptx b/samples/resources/SamplePassword.pptx new file mode 100644 index 000000000..5065dc466 Binary files /dev/null and b/samples/resources/SamplePassword.pptx differ diff --git a/samples/resources/sintel_trailer-480p.ogv b/samples/resources/sintel_trailer-480p.ogv new file mode 100644 index 000000000..2f37321f7 Binary files /dev/null and b/samples/resources/sintel_trailer-480p.ogv differ diff --git a/samples/resources/sintel_trailer-480p.wmv b/samples/resources/sintel_trailer-480p.wmv new file mode 100644 index 000000000..371df2a5e Binary files /dev/null and b/samples/resources/sintel_trailer-480p.wmv differ diff --git a/samples/results/.gitkeep b/samples/results/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/PhpPresentation/AbstractShape.php b/src/PhpPresentation/AbstractShape.php index 552368ba1..4cece57b1 100644 --- a/src/PhpPresentation/AbstractShape.php +++ b/src/PhpPresentation/AbstractShape.php @@ -18,6 +18,7 @@ namespace PhpOffice\PhpPresentation; use PhpOffice\PhpPresentation\Shape\Hyperlink; +use PhpOffice\PhpPresentation\Shape\Placeholder; use PhpOffice\PhpPresentation\Style\Fill; use PhpOffice\PhpPresentation\Style\Shadow; @@ -96,6 +97,12 @@ abstract class AbstractShape implements ComparableInterface */ protected $hyperlink; + /** + * PlaceHolder + * @var \PhpOffice\PhpPresentation\Shape\Placeholder + */ + protected $placeholder; + /** * Hash index * @@ -110,24 +117,27 @@ public function __construct() { // Initialise values $this->container = null; - $this->offsetX = 0; - $this->offsetY = 0; - $this->width = 0; - $this->height = 0; - $this->rotation = 0; - $this->fill = new Style\Fill(); - $this->border = new Style\Border(); - $this->shadow = new Style\Shadow(); + $this->offsetX = 0; + $this->offsetY = 0; + $this->width = 0; + $this->height = 0; + $this->rotation = 0; + $this->fill = new Style\Fill(); + $this->border = new Style\Border(); + $this->shadow = new Style\Shadow(); $this->border->setLineStyle(Style\Border::LINE_NONE); } - + + /** + * Magic Method : clone + */ public function __clone() { $this->container = null; - $this->fill = clone $this->fill; - $this->border = clone $this->border; - $this->shadow = clone $this->shadow; + $this->fill = clone $this->fill; + $this->border = clone $this->border; + $this->shadow = clone $this->shadow; } /** @@ -144,7 +154,7 @@ public function getContainer() * Set Container, Slide or Group * * @param \PhpOffice\PhpPresentation\ShapeContainerInterface $pValue - * @param bool $pOverrideOld If a Slide has already been assigned, overwrite it and remove image from old Slide? + * @param bool $pOverrideOld If a Slide has already been assigned, overwrite it and remove image from old Slide? * @throws \Exception * @return self */ @@ -193,7 +203,7 @@ public function getOffsetX() /** * Set OffsetX * - * @param int $pValue + * @param int $pValue * @return self */ public function setOffsetX($pValue = 0) @@ -216,7 +226,7 @@ public function getOffsetY() /** * Set OffsetY * - * @param int $pValue + * @param int $pValue * @return self */ public function setOffsetY($pValue = 0) @@ -239,7 +249,7 @@ public function getWidth() /** * Set Width * - * @param int $pValue + * @param int $pValue * @return self */ public function setWidth($pValue = 0) @@ -261,7 +271,7 @@ public function getHeight() /** * Set Height * - * @param int $pValue + * @param int $pValue * @return self */ public function setHeight($pValue = 0) @@ -273,14 +283,14 @@ public function setHeight($pValue = 0) /** * Set width and height with proportional resize * - * @param int $width - * @param int $height + * @param int $width + * @param int $height * @example $objDrawing->setWidthAndHeight(160,120); * @return self */ public function setWidthAndHeight($width = 0, $height = 0) { - $this->width = $width; + $this->width = $width; $this->height = $height; return $this; } @@ -298,7 +308,7 @@ public function getRotation() /** * Set Rotation * - * @param int $pValue + * @param int $pValue * @return self */ public function setRotation($pValue = 0) @@ -404,7 +414,7 @@ public function setHyperlink(Hyperlink $pHyperlink = null) */ public function getHashCode() { - return md5((is_object($this->container)?$this->container->getHashCode():'') . $this->offsetX . $this->offsetY . $this->width . $this->height . $this->rotation . $this->getFill()->getHashCode() . (is_null($this->shadow) ? '' : $this->shadow->getHashCode()) . (is_null($this->hyperlink) ? '' : $this->hyperlink->getHashCode()) . __CLASS__); + return md5((is_object($this->container) ? $this->container->getHashCode() : '') . $this->offsetX . $this->offsetY . $this->width . $this->height . $this->rotation . (is_null($this->getFill()) ? '' : $this->getFill()->getHashCode()) . (is_null($this->shadow) ? '' : $this->shadow->getHashCode()) . (is_null($this->hyperlink) ? '' : $this->hyperlink->getHashCode()) . __CLASS__); } /** @@ -432,4 +442,27 @@ public function setHashIndex($value) { $this->hashIndex = $value; } + + public function isPlaceholder() + { + return !is_null($this->placeholder); + } + + public function getPlaceholder() + { + if (!$this->isPlaceholder()) { + return null; + } + return $this->placeholder; + } + + /** + * @param \PhpOffice\PhpPresentation\Shape\Placeholder $placeholder + * @return $this + */ + public function setPlaceHolder(Placeholder $placeholder) + { + $this->placeholder = $placeholder; + return $this; + } } diff --git a/src/PhpPresentation/DocumentLayout.php b/src/PhpPresentation/DocumentLayout.php index 39ed1aa28..75ae86063 100644 --- a/src/PhpPresentation/DocumentLayout.php +++ b/src/PhpPresentation/DocumentLayout.php @@ -150,6 +150,7 @@ public function setDocumentLayout($pValue = self::LAYOUT_SCREEN_4X3, $isLandscap /** * Get Document Layout cx * + * @param string $unit * @return integer */ public function getCX($unit = self::UNIT_EMU) @@ -160,6 +161,7 @@ public function getCX($unit = self::UNIT_EMU) /** * Get Document Layout cy * + * @param string $unit * @return integer */ public function getCY($unit = self::UNIT_EMU) @@ -170,7 +172,9 @@ public function getCY($unit = self::UNIT_EMU) /** * Get Document Layout cx * - * @return integer + * @param float $value + * @param string $unit + * @return DocumentLayout */ public function setCX($value, $unit = self::UNIT_EMU) { @@ -182,7 +186,9 @@ public function setCX($value, $unit = self::UNIT_EMU) /** * Get Document Layout cy * - * @return integer + * @param float $value + * @param string $unit + * @return DocumentLayout */ public function setCY($value, $unit = self::UNIT_EMU) { @@ -193,9 +199,10 @@ public function setCY($value, $unit = self::UNIT_EMU) /** * Convert EMUs to differents units - * @param $value + * @param float $value * @param string $fromUnit * @param string $toUnit + * @return float */ protected function convertUnit($value, $fromUnit, $toUnit) { diff --git a/src/PhpPresentation/HashTable.php b/src/PhpPresentation/HashTable.php index 556e2057b..4e8378ee6 100644 --- a/src/PhpPresentation/HashTable.php +++ b/src/PhpPresentation/HashTable.php @@ -17,8 +17,6 @@ namespace PhpOffice\PhpPresentation; -use PhpOffice\PhpPresentation\ComparableInterface; - /** * \PhpOffice\PhpPresentation\HashTable */ diff --git a/src/PhpPresentation/PhpPresentation.php b/src/PhpPresentation/PhpPresentation.php index 4a3921866..0753924d7 100644 --- a/src/PhpPresentation/PhpPresentation.php +++ b/src/PhpPresentation/PhpPresentation.php @@ -19,6 +19,7 @@ use PhpOffice\PhpPresentation\Slide; use PhpOffice\PhpPresentation\Slide\Iterator; +use PhpOffice\PhpPresentation\Slide\SlideMaster; /** * PhpPresentation @@ -30,53 +31,50 @@ class PhpPresentation * * @var \PhpOffice\PhpPresentation\DocumentProperties */ - private $documentProperties; - + protected $documentProperties; + /** * Presentation properties * * @var \PhpOffice\PhpPresentation\PresentationProperties */ - private $presentationProperties; + protected $presentationProps; /** * Document layout * * @var \PhpOffice\PhpPresentation\DocumentLayout */ - private $layout; + protected $layout; /** * Collection of Slide objects * * @var \PhpOffice\PhpPresentation\Slide[] */ - private $slideCollection = array(); + protected $slideCollection = array(); /** * Active slide index * * @var int */ - private $activeSlideIndex = 0; + protected $activeSlideIndex = 0; /** - * Mark as final - * @var bool + * Collection of Master Slides + * @var \ArrayObject|\PhpOffice\PhpPresentation\Slide\SlideMaster[] */ - private $markAsFinal = false; - - /** - * Zoom - * @var float - */ - private $zoom = 1; + protected $slideMasters; /** * Create a new PhpPresentation with one Slide */ public function __construct() { + // Set empty Master & SlideLayout + $this->createMasterSlide()->createSlideLayout(); + // Initialise slide collection and add one slide $this->createSlide(); $this->setActiveSlideIndex(); @@ -109,7 +107,7 @@ public function setProperties(DocumentProperties $value) { return $this->setDocumentProperties($value); } - + /** * Get properties * @@ -140,7 +138,7 @@ public function setDocumentProperties(DocumentProperties $value) */ public function getPresentationProperties() { - return $this->presentationProperties; + return $this->presentationProps; } /** @@ -151,7 +149,7 @@ public function getPresentationProperties() */ public function setPresentationProperties(PresentationProperties $value) { - $this->presentationProperties = $value; + $this->presentationProps = $value; return $this; } @@ -217,7 +215,7 @@ public function addSlide(Slide $slide = null) /** * Remove slide by index * - * @param int $index Slide index + * @param int $index Slide index * @throws \Exception * @return PhpPresentation */ @@ -235,7 +233,7 @@ public function removeSlideByIndex($index = 0) /** * Get slide by index * - * @param int $index Slide index + * @param int $index Slide index * @return \PhpOffice\PhpPresentation\Slide * @throws \Exception */ @@ -261,11 +259,11 @@ public function getAllSlides() /** * Get index for slide * - * @param \PhpOffice\PhpPresentation\Slide $slide + * @param \PhpOffice\PhpPresentation\Slide\AbstractSlide $slide * @return int * @throws \Exception */ - public function getIndex(Slide $slide) + public function getIndex(Slide\AbstractSlide $slide) { $index = null; foreach ($this->slideCollection as $key => $value) { @@ -300,7 +298,7 @@ public function getActiveSlideIndex() /** * Set active slide index * - * @param int $index Active slide index + * @param int $index Active slide index * @throws \Exception * @return \PhpOffice\PhpPresentation\Slide */ @@ -339,6 +337,32 @@ public function getSlideIterator() return new Iterator($this); } + /** + * Create a masterslide and add it to this presentation + * + * @return \PhpOffice\PhpPresentation\Slide\SlideMaster + */ + public function createMasterSlide() + { + $newMasterSlide = new SlideMaster($this); + $this->addMasterSlide($newMasterSlide); + return $newMasterSlide; + } + + /** + * Add masterslide + * + * @param \PhpOffice\PhpPresentation\Slide\SlideMaster $slide + * @throws \Exception + * @retun \PhpOffice\PhpPresentation\Slide\SlideMaster + */ + public function addMasterSlide(SlideMaster $slide = null) + { + $this->slideMasters[] = $slide; + + return $slide; + } + /** * Copy presentation (!= clone!) * @@ -361,43 +385,61 @@ public function copy() * Mark a document as final * @param bool $state * @return PhpPresentation + * @deprecated for getPresentationProperties()->markAsFinal() */ public function markAsFinal($state = true) { - if (is_bool($state)) { - $this->markAsFinal = $state; - } - return $this; + return $this->getPresentationProperties()->markAsFinal($state); } /** * Return if this document is marked as final * @return bool + * @deprecated for getPresentationProperties()->isMarkedAsFinal() */ public function isMarkedAsFinal() { - return $this->markAsFinal; + return $this->getPresentationProperties()->isMarkedAsFinal(); } /** * Set the zoom of the document (in percentage) * @param float $zoom * @return PhpPresentation + * @deprecated for getPresentationProperties()->setZoom() */ public function setZoom($zoom = 1) { - if (is_numeric($zoom)) { - $this->zoom = $zoom; - } - return $this; + return $this->getPresentationProperties()->setZoom($zoom); } /** * Return the zoom (in percentage) * @return float + * @deprecated for getPresentationProperties()->getZoom() */ public function getZoom() { - return $this->zoom; + return $this->getPresentationProperties()->getZoom(); + } + + /** + * @return \ArrayObject|Slide\SlideMaster[] + */ + public function getAllMasterSlides() + { + return $this->slideMasters; + } + + /** + * @param \ArrayObject|Slide\SlideMaster[] $slideMasters + * @return $this + */ + public function setAllMasterSlides($slideMasters = array()) + { + if ($slideMasters instanceof \ArrayObject || is_array($slideMasters)) { + $this->slideMasters = $slideMasters; + } + return $this; } } diff --git a/src/PhpPresentation/PresentationProperties.php b/src/PhpPresentation/PresentationProperties.php index 749e370fb..106b1f4cc 100644 --- a/src/PhpPresentation/PresentationProperties.php +++ b/src/PhpPresentation/PresentationProperties.php @@ -21,10 +21,57 @@ */ class PresentationProperties { + const VIEW_HANDOUT = 'handoutView'; + const VIEW_NOTES = 'notesView'; + const VIEW_NOTES_MASTER = 'notesMasterView'; + const VIEW_OUTLINE = 'outlineView'; + const VIEW_SLIDE = 'sldView'; + const VIEW_SLIDE_MASTER = 'sldMasterView'; + const VIEW_SLIDE_SORTER = 'sldSorterView'; + const VIEW_SLIDE_THUMBNAIL = 'sldThumbnailView'; + + protected $arrayView = array( + self::VIEW_HANDOUT, + self::VIEW_NOTES, + self::VIEW_NOTES_MASTER, + self::VIEW_OUTLINE, + self::VIEW_SLIDE, + self::VIEW_SLIDE_MASTER, + self::VIEW_SLIDE_SORTER, + self::VIEW_SLIDE_THUMBNAIL, + ); + /* * @var boolean */ protected $isLoopUntilEsc = false; + + /** + * Mark as final + * @var bool + */ + protected $markAsFinal = false; + + /* + * @var string + */ + protected $thumbnail; + + /** + * Zoom + * @var float + */ + protected $zoom = 1; + + /* + * @var string + */ + protected $lastView = self::VIEW_SLIDE; + + /* + * @var boolean + */ + protected $isCommentVisible = false; /** * @return bool @@ -40,7 +87,115 @@ public function isLoopContinuouslyUntilEsc() */ public function setLoopContinuouslyUntilEsc($value = false) { - $this->isLoopUntilEsc = $value; + if (is_bool($value)) { + $this->isLoopUntilEsc = $value; + } + return $this; + } + + /** + * Return the thumbnail file path + * @return string + */ + public function getThumbnailPath() + { + return $this->thumbnail; + } + + /** + * Define the path for the thumbnail file / preview picture + * @param string $path + * @return \PhpOffice\PhpPresentation\PresentationProperties + */ + public function setThumbnailPath($path = '') + { + if (file_exists($path)) { + $this->thumbnail = $path; + } + return $this; + } + + /** + * Mark a document as final + * @param bool $state + * @return PhpPresentation + */ + public function markAsFinal($state = true) + { + if (is_bool($state)) { + $this->markAsFinal = $state; + } + return $this; + } + + /** + * Return if this document is marked as final + * @return bool + */ + public function isMarkedAsFinal() + { + return $this->markAsFinal; + } + + /** + * Set the zoom of the document (in percentage) + * @param float $zoom + * @return PhpPresentation + */ + public function setZoom($zoom = 1) + { + if (is_numeric($zoom)) { + $this->zoom = $zoom; + } + return $this; + } + + /** + * Return the zoom (in percentage) + * @return float + */ + public function getZoom() + { + return $this->zoom; + } + + /** + * @param string $value + * @return $this + */ + public function setLastView($value = self::VIEW_SLIDE) + { + if (in_array($value, $this->arrayView)) { + $this->lastView = $value; + } return $this; } + + /** + * @return string + */ + public function getLastView() + { + return $this->lastView; + } + + /** + * @param bool $value + * @return $this + */ + public function setCommentVisible($value = false) + { + if (is_bool($value)) { + $this->isCommentVisible = $value; + } + return $this; + } + + /** + * @return string + */ + public function isCommentVisible() + { + return $this->isCommentVisible; + } } diff --git a/src/PhpPresentation/Reader/ODPresentation.php b/src/PhpPresentation/Reader/ODPresentation.php index 56bed465b..40c73f45d 100644 --- a/src/PhpPresentation/Reader/ODPresentation.php +++ b/src/PhpPresentation/Reader/ODPresentation.php @@ -21,17 +21,15 @@ use PhpOffice\Common\XMLReader; use PhpOffice\Common\Drawing as CommonDrawing; use PhpOffice\PhpPresentation\PhpPresentation; -use PhpOffice\PhpPresentation\Shape\Hyperlink; -use PhpOffice\PhpPresentation\Shape\MemoryDrawing; +use PhpOffice\PhpPresentation\Shape\Drawing\Gd; +use PhpOffice\PhpPresentation\Shape\RichText; +use PhpOffice\PhpPresentation\Shape\RichText\Paragraph; +use PhpOffice\PhpPresentation\Slide\Background\Image; use PhpOffice\PhpPresentation\Style\Bullet; use PhpOffice\PhpPresentation\Style\Color; use PhpOffice\PhpPresentation\Style\Font; use PhpOffice\PhpPresentation\Style\Shadow; use PhpOffice\PhpPresentation\Style\Alignment; -use PhpOffice\PhpPresentation\Style\PhpOffice\PhpPresentation\Style; -use PhpOffice\PhpPresentation\Shape\RichText; -use PhpOffice\PhpPresentation\Shape\RichText\Paragraph; -use PhpOffice\Common\PhpOffice\Common; /** * Serialized format reader @@ -161,7 +159,7 @@ protected function loadDocumentProperties() '/office:document-meta/office:meta/meta:creation-date' => 'setCreated', '/office:document-meta/office:meta/dc:date' => 'setModified', ); - $oProperties = $this->oPhpPresentation->getProperties(); + $oProperties = $this->oPhpPresentation->getDocumentProperties(); foreach ($arrayProperties as $path => $property) { if (is_object($oElement = $this->oXMLReader->getElement($path))) { if (in_array($property, array('setCreated', 'setModified'))) { @@ -217,7 +215,7 @@ protected function loadStyle(\DOMElement $nodeStyle) $contentImg = $this->oZip->getFromName($this->arrayCommonStyles[$nameStyle]['path']); file_put_contents($tmpBkgImg, $contentImg); - $oBackground = new \PhpOffice\PhpPresentation\Slide\Background\Image(); + $oBackground = new Image(); $oBackground->setPath($tmpBkgImg); } } @@ -365,9 +363,9 @@ protected function loadSlide(\DOMElement $nodeSlide) protected function loadShapeDrawing(\DOMElement $oNodeFrame) { // Core - $oShape = new MemoryDrawing(); + $oShape = new Gd(); $oShape->getShadow()->setVisible(false); - + $oNodeImage = $this->oXMLReader->getElement('draw:image', $oNodeFrame); if ($oNodeImage) { if ($oNodeImage->hasAttribute('xlink:href')) { diff --git a/src/PhpPresentation/Reader/PowerPoint2007.php b/src/PhpPresentation/Reader/PowerPoint2007.php index ffead7ec5..3e5bf61f9 100644 --- a/src/PhpPresentation/Reader/PowerPoint2007.php +++ b/src/PhpPresentation/Reader/PowerPoint2007.php @@ -17,14 +17,22 @@ namespace PhpOffice\PhpPresentation\Reader; +use PhpOffice\PhpPresentation\DocumentLayout; +use PhpOffice\PhpPresentation\Shape\RichText\Paragraph; +use PhpOffice\PhpPresentation\Slide\AbstractSlide; +use PhpOffice\PhpPresentation\Shape\Placeholder; +use PhpOffice\PhpPresentation\Slide\Background\Image; +use PhpOffice\PhpPresentation\Slide\SlideLayout; +use PhpOffice\PhpPresentation\Slide\SlideMaster; +use PhpOffice\PhpPresentation\Shape\Drawing\Gd; +use PhpOffice\PhpPresentation\Style\SchemeColor; +use PhpOffice\PhpPresentation\Style\TextStyle; use ZipArchive; use PhpOffice\Common\XMLReader; use PhpOffice\Common\Drawing as CommonDrawing; use PhpOffice\PhpPresentation\PhpPresentation; -use PhpOffice\PhpPresentation\Shape\MemoryDrawing; use PhpOffice\PhpPresentation\Style\Bullet; use PhpOffice\PhpPresentation\Style\Color; -use PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack\TemplateBased; /** * Serialized format reader @@ -42,9 +50,13 @@ class PowerPoint2007 implements ReaderInterface */ protected $oZip; /** - * @var string[] + * @var array[] */ protected $arrayRels = array(); + /** + * @var SlideLayout[] + */ + protected $arraySlideLayouts = array(); /* * @var string */ @@ -65,7 +77,7 @@ public function canRead($pFilename) /** * Does a file support UnserializePhpPresentation ? * - * @param string $pFilename + * @param string $pFilename * @throws \Exception * @return boolean */ @@ -75,7 +87,7 @@ public function fileSupportsUnserializePhpPresentation($pFilename = '') if (!file_exists($pFilename)) { throw new \Exception("Could not open " . $pFilename . " for reading! File does not exist."); } - + $oZip = new ZipArchive(); // Is it a zip ? if ($oZip->open($pFilename) === true) { @@ -91,7 +103,7 @@ public function fileSupportsUnserializePhpPresentation($pFilename = '') /** * Loads PhpPresentation Serialized file * - * @param string $pFilename + * @param string $pFilename * @return \PhpOffice\PhpPresentation\PhpPresentation * @throws \Exception */ @@ -108,15 +120,16 @@ public function load($pFilename) /** * Load PhpPresentation Serialized file * - * @param string $pFilename + * @param string $pFilename * @return \PhpOffice\PhpPresentation\PhpPresentation */ protected function loadFile($pFilename) { $this->oPhpPresentation = new PhpPresentation(); $this->oPhpPresentation->removeSlideByIndex(); + $this->oPhpPresentation->setAllMasterSlides(array()); $this->filename = $pFilename; - + $this->oZip = new ZipArchive(); $this->oZip->open($this->filename); $docPropsCore = $this->oZip->getFromName('docProps/core.xml'); @@ -136,12 +149,37 @@ protected function loadFile($pFilename) $pptPresentation = $this->oZip->getFromName('ppt/presentation.xml'); if ($pptPresentation !== false) { + $this->loadDocumentLayout($pptPresentation); $this->loadSlides($pptPresentation); } return $this->oPhpPresentation; } - + + /** + * Read Document Layout + * @param $sPart + */ + protected function loadDocumentLayout($sPart) + { + $xmlReader = new XMLReader(); + if ($xmlReader->getDomFromString($sPart)) { + foreach ($xmlReader->getElements('/p:presentation/p:sldSz') as $oElement) { + $type = $oElement->getAttribute('type'); + $oLayout = $this->oPhpPresentation->getLayout(); + if ($type == DocumentLayout::LAYOUT_CUSTOM) { + $oLayout->setCX($oElement->getAttribute('cx')); + $oLayout->setCY($oElement->getAttribute('cy')); + } else { + $oLayout->setDocumentLayout($type, true); + if ($oElement->getAttribute('cx') < $oElement->getAttribute('cy')) { + $oLayout->setDocumentLayout($type, false); + } + } + } + } + } + /** * Read Document Properties * @param string $sPart @@ -151,17 +189,17 @@ protected function loadDocumentProperties($sPart) $xmlReader = new XMLReader(); if ($xmlReader->getDomFromString($sPart)) { $arrayProperties = array( - '/cp:coreProperties/dc:creator' => 'setCreator', - '/cp:coreProperties/cp:lastModifiedBy' => 'setLastModifiedBy', - '/cp:coreProperties/dc:title' => 'setTitle', - '/cp:coreProperties/dc:description' => 'setDescription', - '/cp:coreProperties/dc:subject' => 'setSubject', - '/cp:coreProperties/cp:keywords' => 'setKeywords', - '/cp:coreProperties/cp:category' => 'setCategory', - '/cp:coreProperties/dcterms:created' => 'setCreated', - '/cp:coreProperties/dcterms:modified' => 'setModified', + '/cp:coreProperties/dc:creator' => 'setCreator', + '/cp:coreProperties/cp:lastModifiedBy' => 'setLastModifiedBy', + '/cp:coreProperties/dc:title' => 'setTitle', + '/cp:coreProperties/dc:description' => 'setDescription', + '/cp:coreProperties/dc:subject' => 'setSubject', + '/cp:coreProperties/cp:keywords' => 'setKeywords', + '/cp:coreProperties/cp:category' => 'setCategory', + '/cp:coreProperties/dcterms:created' => 'setCreated', + '/cp:coreProperties/dcterms:modified' => 'setModified', ); - $oProperties = $this->oPhpPresentation->getProperties(); + $oProperties = $this->oPhpPresentation->getDocumentProperties(); foreach ($arrayProperties as $path => $property) { if (is_object($oElement = $xmlReader->getElement($path))) { if ($oElement->hasAttribute('xsi:type') && $oElement->getAttribute('xsi:type') == 'dcterms:W3CDTF') { @@ -188,7 +226,7 @@ protected function loadCustomProperties($sPart) $pathMarkAsFinal = '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="_MarkAsFinal"]/vt:bool'; if (is_object($oElement = $xmlReader->getElement($pathMarkAsFinal))) { if ($oElement->nodeValue == 'true') { - $this->oPhpPresentation->markAsFinal(true); + $this->oPhpPresentation->getPresentationProperties()->markAsFinal(true); } } } @@ -205,12 +243,12 @@ protected function loadViewProperties($sPart) $pathZoom = '/p:viewPr/p:slideViewPr/p:cSldViewPr/p:cViewPr/p:scale/a:sx'; if (is_object($oElement = $xmlReader->getElement($pathZoom))) { if ($oElement->hasAttribute('d') && $oElement->hasAttribute('n')) { - $this->oPhpPresentation->setZoom($oElement->getAttribute('n') / $oElement->getAttribute('d')); + $this->oPhpPresentation->getPresentationProperties()->setZoom($oElement->getAttribute('n') / $oElement->getAttribute('d')); } } } } - + /** * Extract all slides */ @@ -220,13 +258,16 @@ protected function loadSlides($sPart) if ($xmlReader->getDomFromString($sPart)) { $fileRels = 'ppt/_rels/presentation.xml.rels'; $this->loadRels($fileRels); + // Load the Masterslides + $this->loadMasterSlides($xmlReader, $fileRels); + // Continue with loading the slides foreach ($xmlReader->getElements('/p:presentation/p:sldIdLst/p:sldId') as $oElement) { $rId = $oElement->getAttribute('r:id'); $pathSlide = isset($this->arrayRels[$fileRels][$rId]) ? $this->arrayRels[$fileRels][$rId]['Target'] : ''; if (!empty($pathSlide)) { - $pptSlide = $this->oZip->getFromName('ppt/'.$pathSlide); + $pptSlide = $this->oZip->getFromName('ppt/' . $pathSlide); if ($pptSlide !== false) { - $this->loadRels('ppt/slides/_rels/'.basename($pathSlide).'.rels'); + $this->loadRels('ppt/slides/_rels/' . basename($pathSlide) . '.rels'); $this->loadSlide($pptSlide, basename($pathSlide)); } } @@ -234,6 +275,29 @@ protected function loadSlides($sPart) } } + /** + * Extract all MasterSlides + * @param XMLReader $xmlReader + * @param $fileRels + */ + protected function loadMasterSlides(XMLReader $xmlReader, $fileRels) + { + // Get all the MasterSlide Id's from the presentation.xml file + foreach ($xmlReader->getElements('/p:presentation/p:sldMasterIdLst/p:sldMasterId') as $oElement) { + $rId = $oElement->getAttribute('r:id'); + // Get the path to the masterslide from the array with _rels files + $pathMasterSlide = isset($this->arrayRels[$fileRels][$rId]) ? + $this->arrayRels[$fileRels][$rId]['Target'] : ''; + if (!empty($pathMasterSlide)) { + $pptMasterSlide = $this->oZip->getFromName('ppt/' . $pathMasterSlide); + if ($pptMasterSlide !== false) { + $this->loadRels('ppt/slideMasters/_rels/' . basename($pathMasterSlide) . '.rels'); + $this->loadMasterSlide($pptMasterSlide, basename($pathMasterSlide)); + } + } + } + } + /** * Extract data from slide * @param string $sPart @@ -244,8 +308,9 @@ protected function loadSlide($sPart, $baseFile) $xmlReader = new XMLReader(); if ($xmlReader->getDomFromString($sPart)) { // Core - $this->oPhpPresentation->createSlide(); + $oSlide = $this->oPhpPresentation->createSlide(); $this->oPhpPresentation->setActiveSlideIndex($this->oPhpPresentation->getSlideCount() - 1); + $oSlide->setRelsIndex('ppt/slides/_rels/' . $baseFile . '.rels'); // Background $oElement = $xmlReader->getElement('/p:sld/p:cSld/p:bg/p:bgPr'); @@ -264,10 +329,10 @@ protected function loadSlide($sPart, $baseFile) } $oElementImage = $xmlReader->getElement('a:blipFill/a:blip', $oElement); if ($oElementImage) { - $relImg = $this->arrayRels['ppt/slides/_rels/'.$baseFile.'.rels'][$oElementImage->getAttribute('r:embed')]; + $relImg = $this->arrayRels['ppt/slides/_rels/' . $baseFile . '.rels'][$oElementImage->getAttribute('r:embed')]; if (is_array($relImg)) { // File - $pathImage = 'ppt/slides/'.$relImg['Target']; + $pathImage = 'ppt/slides/' . $relImg['Target']; $pathImage = explode('/', $pathImage); foreach ($pathImage as $key => $partPath) { if ($partPath == '..') { @@ -281,7 +346,7 @@ protected function loadSlide($sPart, $baseFile) $tmpBkgImg = tempnam(sys_get_temp_dir(), 'PhpPresentationReaderPpt2007Bkg'); file_put_contents($tmpBkgImg, $contentImg); // Background - $oBackground = new \PhpOffice\PhpPresentation\Slide\Background\Image(); + $oBackground = new Image(); $oBackground->setPath($tmpBkgImg); // Slide Background $oSlide = $this->oPhpPresentation->getActiveSlide(); @@ -294,55 +359,314 @@ protected function loadSlide($sPart, $baseFile) foreach ($xmlReader->getElements('/p:sld/p:cSld/p:spTree/*') as $oNode) { switch ($oNode->tagName) { case 'p:pic': - $this->loadShapeDrawing($xmlReader, $oNode, $baseFile); + $this->loadShapeDrawing($xmlReader, $oNode, $oSlide); break; case 'p:sp': - $this->loadShapeRichText($xmlReader, $oNode, $baseFile); + $this->loadShapeRichText($xmlReader, $oNode, $oSlide); break; default: - //var_export($oNode->tagName); + //var_export($oNode->tagName); } } // Layout - $oLayoutPack = new TemplateBased($this->filename); $oSlide = $this->oPhpPresentation->getActiveSlide(); - foreach ($this->arrayRels['ppt/slides/_rels/'.$baseFile.'.rels'] as $valueRel) { + foreach ($this->arrayRels['ppt/slides/_rels/' . $baseFile . '.rels'] as $valueRel) { if ($valueRel['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout') { - $layoutId = $valueRel['Target']; - $layoutId = str_replace('../slideLayouts/slideLayout', '', $layoutId); - $layoutId = str_replace('.xml', '', $layoutId); - $layoutName = $oLayoutPack->findLayoutName((int)$layoutId, $oSlide->getSlideMasterId()); - $oSlide->setSlideLayout($layoutName); + $layoutBasename = basename($valueRel['Target']); + if (array_key_exists($layoutBasename, $this->arraySlideLayouts)) { + $oSlide->setSlideLayout($this->arraySlideLayouts[$layoutBasename]); + } break; } } } } - + + private function loadMasterSlide($sPart, $baseFile) + { + $xmlReader = new XMLReader(); + if ($xmlReader->getDomFromString($sPart)) { + // Core + $oSlideMaster = $this->oPhpPresentation->createMasterSlide(); + $oSlideMaster->setTextStyles(new TextStyle(false)); + $oSlideMaster->setRelsIndex('ppt/slideMasters/_rels/' . $baseFile . '.rels'); + + // Background + $oElement = $xmlReader->getElement('/p:sldMaster/p:cSld/p:bg'); + if ($oElement) { + $this->loadSlideBackground($xmlReader, $oElement, $oSlideMaster); + } + + // Shapes + $arrayElements = $xmlReader->getElements('/p:sldMaster/p:cSld/p:spTree/*'); + if ($arrayElements) { + $this->loadSlideShapes($oSlideMaster, $arrayElements, $xmlReader); + } + // Header & Footer + + // ColorMapping + $colorMap = array(); + $oElement = $xmlReader->getElement('/p:sldMaster/p:clrMap'); + if ($oElement->hasAttributes()) { + foreach ($oElement->attributes as $attr) { + $colorMap[$attr->nodeName] = $attr->nodeValue; + } + $oSlideMaster->colorMap->setMapping($colorMap); + } + + // TextStyles + $arrayElementTxStyles = $xmlReader->getElements('/p:sldMaster/p:txStyles/*'); + if ($arrayElementTxStyles) { + foreach ($arrayElementTxStyles as $oElementTxStyle) { + $arrayElementsLvl = $xmlReader->getElements('/p:sldMaster/p:txStyles/'.$oElementTxStyle->nodeName.'/*'); + foreach ($arrayElementsLvl as $oElementLvl) { + if ($oElementLvl->nodeName == 'a:extLst') { + continue; + } + $oRTParagraph = new Paragraph(); + + if ($oElementLvl->nodeName == 'a:defPPr') { + $level = 0; + } else { + $level = str_replace('a:lvl', '', $oElementLvl->nodeName); + $level = str_replace('pPr', '', $level); + } + + if ($oElementLvl->hasAttribute('algn')) { + $oRTParagraph->getAlignment()->setHorizontal($oElementLvl->getAttribute('algn')); + } + if ($oElementLvl->hasAttribute('marL')) { + $val = $oElementLvl->getAttribute('marL'); + $val = CommonDrawing::emuToPixels($val); + $oRTParagraph->getAlignment()->setMarginLeft($val); + } + if ($oElementLvl->hasAttribute('marR')) { + $val = $oElementLvl->getAttribute('marR'); + $val = CommonDrawing::emuToPixels($val); + $oRTParagraph->getAlignment()->setMarginRight($val); + } + if ($oElementLvl->hasAttribute('indent')) { + $val = $oElementLvl->getAttribute('indent'); + $val = CommonDrawing::emuToPixels($val); + $oRTParagraph->getAlignment()->setIndent($val); + } + $oElementLvlDefRPR = $xmlReader->getElement('a:defRPr', $oElementLvl); + if ($oElementLvlDefRPR) { + if ($oElementLvlDefRPR->hasAttribute('sz')) { + $oRTParagraph->getFont()->setSize($oElementLvlDefRPR->getAttribute('sz') / 100); + } + if ($oElementLvlDefRPR->hasAttribute('b') && $oElementLvlDefRPR->getAttribute('b') == 1) { + $oRTParagraph->getFont()->setBold(true); + } + if ($oElementLvlDefRPR->hasAttribute('i') && $oElementLvlDefRPR->getAttribute('i') == 1) { + $oRTParagraph->getFont()->setItalic(true); + } + } + $oElementSchemeColor = $xmlReader->getElement('a:defRPr/a:solidFill/a:schemeClr', $oElementLvl); + if ($oElementSchemeColor) { + if ($oElementSchemeColor->hasAttribute('val')) { + $oRTParagraph->getFont()->setColor(new SchemeColor())->getColor()->setValue($oElementSchemeColor->getAttribute('val')); + } + } + + switch ($oElementTxStyle->nodeName) { + case 'p:bodyStyle': + $oSlideMaster->getTextStyles()->setBodyStyleAtLvl($oRTParagraph, $level); + break; + case 'p:otherStyle': + $oSlideMaster->getTextStyles()->setOtherStyleAtLvl($oRTParagraph, $level); + break; + case 'p:titleStyle': + $oSlideMaster->getTextStyles()->setTitleStyleAtLvl($oRTParagraph, $level); + break; + } + } + } + } + + // Load the theme + foreach ($this->arrayRels[$oSlideMaster->getRelsIndex()] as $arrayRel) { + if ($arrayRel['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme') { + $pptTheme = $this->oZip->getFromName('ppt/' . substr($arrayRel['Target'], strrpos($arrayRel['Target'], '../') + 3)); + if ($pptTheme !== false) { + $this->loadTheme($pptTheme, $oSlideMaster); + } + break; + } + } + + // Load the Layoutslide + foreach ($xmlReader->getElements('/p:sldMaster/p:sldLayoutIdLst/p:sldLayoutId') as $oElement) { + $rId = $oElement->getAttribute('r:id'); + // Get the path to the masterslide from the array with _rels files + $pathLayoutSlide = isset($this->arrayRels[$oSlideMaster->getRelsIndex()][$rId]) ? + $this->arrayRels[$oSlideMaster->getRelsIndex()][$rId]['Target'] : ''; + if (!empty($pathLayoutSlide)) { + $pptLayoutSlide = $this->oZip->getFromName('ppt/' . substr($pathLayoutSlide, strrpos($pathLayoutSlide, '../') + 3)); + if ($pptLayoutSlide !== false) { + $this->loadRels('ppt/slideLayouts/_rels/' . basename($pathLayoutSlide) . '.rels'); + $oSlideMaster->addSlideLayout( + $this->loadLayoutSlide($pptLayoutSlide, basename($pathLayoutSlide), $oSlideMaster) + ); + } + } + } + } + } + + private function loadLayoutSlide($sPart, $baseFile, SlideMaster $oSlideMaster) + { + $xmlReader = new XMLReader(); + if ($xmlReader->getDomFromString($sPart)) { + // Core + $oSlideLayout = new SlideLayout($oSlideMaster); + $oSlideLayout->setRelsIndex('ppt/slideLayouts/_rels/' . $baseFile . '.rels'); + + // Name + $oElement = $xmlReader->getElement('/p:sldLayout/p:cSld'); + if ($oElement && $oElement->hasAttribute('name')) { + $oSlideLayout->setLayoutName($oElement->getAttribute('name')); + } + + // Background + $oElement = $xmlReader->getElement('/p:sldLayout/p:cSld/p:bg'); + if ($oElement) { + $this->loadSlideBackground($xmlReader, $oElement, $oSlideLayout); + } + + // ColorMapping + $oElement = $xmlReader->getElement('/p:sldLayout/p:clrMapOvr/a:overrideClrMapping'); + if ($oElement && $oElement->hasAttributes()) { + $colorMap = array(); + foreach ($oElement->attributes as $attr) { + $colorMap[$attr->nodeName] = $attr->nodeValue; + } + $oSlideLayout->colorMap->setMapping($colorMap); + } + + // Shapes + $oElements = $xmlReader->getElements('/p:sldLayout/p:cSld/p:spTree/*'); + if ($oElements) { + $this->loadSlideShapes($oSlideLayout, $oElements, $xmlReader); + } + $this->arraySlideLayouts[$baseFile] = &$oSlideLayout; + return $oSlideLayout; + } + return null; + } + + /** + * @param string $sPart + * @param SlideMaster $oSlideMaster + */ + private function loadTheme($sPart, SlideMaster $oSlideMaster) + { + $xmlReader = new XMLReader(); + if ($xmlReader->getDomFromString($sPart)) { + $oElements = $xmlReader->getElements('/a:theme/a:themeElements/a:clrScheme/*'); + if ($oElements) { + foreach ($oElements as $oElement) { + $oSchemeColor = new SchemeColor(); + $oSchemeColor->setValue(str_replace('a:', '', $oElement->tagName)); + $colorElement = $xmlReader->getElement('*', $oElement); + if ($colorElement) { + if ($colorElement->hasAttribute('lastClr')) { + $oSchemeColor->setRGB($colorElement->getAttribute('lastClr')); + } elseif ($colorElement->hasAttribute('val')) { + $oSchemeColor->setRGB($colorElement->getAttribute('val')); + } + } + $oSlideMaster->addSchemeColor($oSchemeColor); + } + } + } + } + + /** + * @param XMLReader $xmlReader + * @param \DOMElement $oElement + * @param AbstractSlide $oSlide + */ + private function loadSlideBackground(XMLReader $xmlReader, \DOMElement $oElement, AbstractSlide $oSlide) + { + // Background color + $oElementColor = $xmlReader->getElement('p:bgPr/a:solidFill/a:srgbClr', $oElement); + if ($oElementColor) { + // Color + $oColor = new Color(); + $oColor->setRGB($oElementColor->hasAttribute('val') ? $oElementColor->getAttribute('val') : null); + // Background + $oBackground = new \PhpOffice\PhpPresentation\Slide\Background\Color(); + $oBackground->setColor($oColor); + // Slide Background + $oSlide->setBackground($oBackground); + } + + // Background scheme color + $oElementSchemeColor = $xmlReader->getElement('p:bgRef/a:schemeClr', $oElement); + if ($oElementSchemeColor) { + // Color + $oColor = new SchemeColor(); + $oColor->setValue($oElementSchemeColor->hasAttribute('val') ? $oElementSchemeColor->getAttribute('val') : null); + // Background + $oBackground = new \PhpOffice\PhpPresentation\Slide\Background\SchemeColor(); + $oBackground->setSchemeColor($oColor); + // Slide Background + $oSlide->setBackground($oBackground); + } + + // Background image + $oElementImage = $xmlReader->getElement('p:bgPr/a:blipFill/a:blip', $oElement); + if ($oElementImage) { + $relImg = $this->arrayRels[$oSlide->getRelsIndex()][$oElementImage->getAttribute('r:embed')]; + if (is_array($relImg)) { + // File + $pathImage = 'ppt/slides/' . $relImg['Target']; + $pathImage = explode('/', $pathImage); + foreach ($pathImage as $key => $partPath) { + if ($partPath == '..') { + unset($pathImage[$key - 1]); + unset($pathImage[$key]); + } + } + $pathImage = implode('/', $pathImage); + $contentImg = $this->oZip->getFromName($pathImage); + + $tmpBkgImg = tempnam(sys_get_temp_dir(), 'PhpPresentationReaderPpt2007Bkg'); + file_put_contents($tmpBkgImg, $contentImg); + // Background + $oBackground = new Image(); + $oBackground->setPath($tmpBkgImg); + // Slide Background + $oSlide->setBackground($oBackground); + } + } + } + /** * * @param XMLReader $document * @param \DOMElement $node - * @param string $baseFile + * @param AbstractSlide $oSlide */ - protected function loadShapeDrawing(XMLReader $document, \DOMElement $node, $baseFile) + protected function loadShapeDrawing(XMLReader $document, \DOMElement $node, AbstractSlide $oSlide) { // Core - $oShape = new MemoryDrawing(); + $oShape = new Gd(); $oShape->getShadow()->setVisible(false); // Variables - $fileRels = 'ppt/slides/_rels/'.$baseFile.'.rels'; - + $fileRels = $oSlide->getRelsIndex(); + $oElement = $document->getElement('p:nvPicPr/p:cNvPr', $node); if ($oElement) { $oShape->setName($oElement->hasAttribute('name') ? $oElement->getAttribute('name') : ''); $oShape->setDescription($oElement->hasAttribute('descr') ? $oElement->getAttribute('descr') : ''); } - + $oElement = $document->getElement('p:blipFill/a:blip', $node); if ($oElement) { if ($oElement->hasAttribute('r:embed') && isset($this->arrayRels[$fileRels][$oElement->getAttribute('r:embed')]['Target'])) { - $pathImage = 'ppt/slides/'.$this->arrayRels[$fileRels][$oElement->getAttribute('r:embed')]['Target']; + $pathImage = 'ppt/slides/' . $this->arrayRels[$fileRels][$oElement->getAttribute('r:embed')]['Target']; $pathImage = explode('/', $pathImage); foreach ($pathImage as $key => $partPath) { if ($partPath == '..') { @@ -357,14 +681,14 @@ protected function loadShapeDrawing(XMLReader $document, \DOMElement $node, $bas } } } - + $oElement = $document->getElement('p:spPr/a:xfrm', $node); if ($oElement) { if ($oElement->hasAttribute('rot')) { $oShape->setRotation(CommonDrawing::angleToDegrees($oElement->getAttribute('rot'))); } } - + $oElement = $document->getElement('p:spPr/a:xfrm/a:off', $node); if ($oElement) { if ($oElement->hasAttribute('x')) { @@ -374,7 +698,7 @@ protected function loadShapeDrawing(XMLReader $document, \DOMElement $node, $bas $oShape->setOffsetY(CommonDrawing::emuToPixels($oElement->getAttribute('y'))); } } - + $oElement = $document->getElement('p:spPr/a:xfrm/a:ext', $node); if ($oElement) { if ($oElement->hasAttribute('cx')) { @@ -384,11 +708,11 @@ protected function loadShapeDrawing(XMLReader $document, \DOMElement $node, $bas $oShape->setHeight(CommonDrawing::emuToPixels($oElement->getAttribute('cy'))); } } - + $oElement = $document->getElement('p:spPr/a:effectLst', $node); if ($oElement) { $oShape->getShadow()->setVisible(true); - + $oSubElement = $document->getElement('a:outerShdw', $oElement); if ($oSubElement) { if ($oSubElement->hasAttribute('blurRad')) { @@ -404,7 +728,7 @@ protected function loadShapeDrawing(XMLReader $document, \DOMElement $node, $bas $oShape->getShadow()->setAlignment($oSubElement->getAttribute('algn')); } } - + $oSubElement = $document->getElement('a:outerShdw/a:srgbClr', $oElement); if ($oSubElement) { if ($oSubElement->hasAttribute('val')) { @@ -413,31 +737,37 @@ protected function loadShapeDrawing(XMLReader $document, \DOMElement $node, $bas $oShape->getShadow()->setColor($oColor); } } - + $oSubElement = $document->getElement('a:outerShdw/a:srgbClr/a:alpha', $oElement); if ($oSubElement) { if ($oSubElement->hasAttribute('val')) { - $oShape->getShadow()->setAlpha((int) $oSubElement->getAttribute('val') / 1000); + $oShape->getShadow()->setAlpha((int)$oSubElement->getAttribute('val') / 1000); } } } - - $this->oPhpPresentation->getActiveSlide()->addShape($oShape); + + $oSlide->addShape($oShape); } - - protected function loadShapeRichText(XMLReader $document, \DOMElement $node, $baseFile) + + /** + * @param XMLReader $document + * @param \DOMElement $node + * @param AbstractSlide $oSlide + * @throws \Exception + */ + protected function loadShapeRichText(XMLReader $document, \DOMElement $node, AbstractSlide $oSlide) { // Core - $oShape = $this->oPhpPresentation->getActiveSlide()->createRichTextShape(); + $oShape = $oSlide->createRichTextShape(); $oShape->setParagraphs(array()); // Variables - $fileRels = 'ppt/slides/_rels/'.$baseFile.'.rels'; - + $fileRels = $oSlide->getRelsIndex(); + $oElement = $document->getElement('p:spPr/a:xfrm', $node); if ($oElement && $oElement->hasAttribute('rot')) { $oShape->setRotation(CommonDrawing::angleToDegrees($oElement->getAttribute('rot'))); } - + $oElement = $document->getElement('p:spPr/a:xfrm/a:off', $node); if ($oElement) { if ($oElement->hasAttribute('x')) { @@ -447,7 +777,7 @@ protected function loadShapeRichText(XMLReader $document, \DOMElement $node, $ba $oShape->setOffsetY(CommonDrawing::emuToPixels($oElement->getAttribute('y'))); } } - + $oElement = $document->getElement('p:spPr/a:xfrm/a:ext', $node); if ($oElement) { if ($oElement->hasAttribute('cx')) { @@ -457,13 +787,21 @@ protected function loadShapeRichText(XMLReader $document, \DOMElement $node, $ba $oShape->setHeight(CommonDrawing::emuToPixels($oElement->getAttribute('cy'))); } } - + + $oElement = $document->getElement('p:nvSpPr/p:nvPr/p:ph', $node); + if ($oElement) { + if ($oElement->hasAttribute('type')) { + $placeholder = new Placeholder($oElement->getAttribute('type')); + $oShape->setPlaceHolder($placeholder); + } + } + $arrayElements = $document->getElements('p:txBody/a:p', $node); foreach ($arrayElements as $oElement) { // Core $oParagraph = $oShape->createParagraph(); $oParagraph->setRichTextElements(array()); - + $oSubElement = $document->getElement('a:pPr', $oElement); if ($oSubElement) { if ($oSubElement->hasAttribute('algn')) { @@ -484,9 +822,10 @@ protected function loadShapeRichText(XMLReader $document, \DOMElement $node, $ba if ($oSubElement->hasAttribute('lvl')) { $oParagraph->getAlignment()->setLevel($oSubElement->getAttribute('lvl')); } - - $oElementBuFont = $document->getElement('a:buFont', $oSubElement); + $oParagraph->getBulletStyle()->setBulletType(Bullet::TYPE_NONE); + + $oElementBuFont = $document->getElement('a:buFont', $oSubElement); if ($oElementBuFont) { if ($oElementBuFont->hasAttribute('typeface')) { $oParagraph->getBulletStyle()->setBulletFont($oElementBuFont->getAttribute('typeface')); @@ -499,7 +838,7 @@ protected function loadShapeRichText(XMLReader $document, \DOMElement $node, $ba $oParagraph->getBulletStyle()->setBulletChar($oElementBuChar->getAttribute('char')); } } - /*$oElementBuAutoNum = $document->getElement('a:buAutoNum', $oSubElement); + $oElementBuAutoNum = $document->getElement('a:buAutoNum', $oSubElement); if ($oElementBuAutoNum) { $oParagraph->getBulletStyle()->setBulletType(Bullet::TYPE_NUMERIC); if ($oElementBuAutoNum->hasAttribute('type')) { @@ -508,7 +847,19 @@ protected function loadShapeRichText(XMLReader $document, \DOMElement $node, $ba if ($oElementBuAutoNum->hasAttribute('startAt') && $oElementBuAutoNum->getAttribute('startAt') != 1) { $oParagraph->getBulletStyle()->setBulletNumericStartAt($oElementBuAutoNum->getAttribute('startAt')); } - }*/ + } + $oElementBuClr = $document->getElement('a:buClr', $oSubElement); + if ($oElementBuClr) { + $oColor = new Color(); + /** + * @todo Create protected for reading Color + */ + $oElementColor = $document->getElement('a:srgbClr', $oElementBuClr); + if ($oElementColor) { + $oColor->setRGB($oElementColor->hasAttribute('val') ? $oElementColor->getAttribute('val') : null); + } + $oParagraph->getBulletStyle()->setBulletColor($oColor); + } } $arraySubElements = $document->getElements('(a:r|a:br)', $oElement); foreach ($arraySubElements as $oSubElement) { @@ -552,21 +903,21 @@ protected function loadShapeRichText(XMLReader $document, \DOMElement $node, $ba $oText->getHyperlink()->setUrl($this->arrayRels[$fileRels][$oElementHlinkClick->getAttribute('r:id')]['Target']); } } - //} else { + //} else { // $oText = $oParagraph->createText(); - } - $oSubSubElement = $document->getElement('a:t', $oSubElement); - $oText->setText($oSubSubElement->nodeValue); + $oSubSubElement = $document->getElement('a:t', $oSubElement); + $oText->setText($oSubSubElement->nodeValue); + } } } } - + if (count($oShape->getParagraphs()) > 0) { $oShape->setActiveParagraph(0); } } - + /** * * @param string $fileRels @@ -587,4 +938,26 @@ protected function loadRels($fileRels) } } } + + /** + * @param $oSlide + * @param $oElements + * @param $xmlReader + * @internal param $baseFile + */ + private function loadSlideShapes($oSlide, $oElements, $xmlReader) + { + foreach ($oElements as $oNode) { + switch ($oNode->tagName) { + case 'p:pic': + $this->loadShapeDrawing($xmlReader, $oNode, $oSlide); + break; + case 'p:sp': + $this->loadShapeRichText($xmlReader, $oNode, $oSlide); + break; + default: + //var_export($oNode->tagName); + } + } + } } diff --git a/src/PhpPresentation/Reader/PowerPoint97.php b/src/PhpPresentation/Reader/PowerPoint97.php index c2215e622..d0ef5d2a7 100644 --- a/src/PhpPresentation/Reader/PowerPoint97.php +++ b/src/PhpPresentation/Reader/PowerPoint97.php @@ -18,19 +18,18 @@ namespace PhpOffice\PhpPresentation\Reader; use PhpOffice\Common\Microsoft\OLERead; -use PhpOffice\PhpPresentation\Shape\Drawing; +use PhpOffice\Common\Text; use PhpOffice\PhpPresentation\PhpPresentation; -use PhpOffice\PhpPresentation\Shape\MemoryDrawing; -use PhpOffice\PhpPresentation\Style\Alignment; -use PhpOffice\PhpPresentation\Style\Color; -use PhpOffice\PhpPresentation\Shape\RichText; use PhpOffice\PhpPresentation\AbstractShape; -use PhpOffice\PhpPresentation\Style\Bullet; +use PhpOffice\PhpPresentation\Shape; +use PhpOffice\PhpPresentation\Shape\Drawing; +use PhpOffice\PhpPresentation\Shape\Group; use PhpOffice\PhpPresentation\Shape\Hyperlink; use PhpOffice\PhpPresentation\Shape\Line; -use PhpOffice\Common\Text; -use PhpOffice\PhpPresentation\Shape\Group; -use PhpOffice\PhpPresentation\Shape\PhpOffice\PhpPresentation\Shape; +use PhpOffice\PhpPresentation\Shape\RichText; +use PhpOffice\PhpPresentation\Style\Alignment; +use PhpOffice\PhpPresentation\Style\Color; +use PhpOffice\PhpPresentation\Style\Bullet; /** * Serialized format reader @@ -465,10 +464,10 @@ private function loadPicturesStream() $stream = $this->streamPictures; $pos = 0; - $readSuccess = true; do { $arrayRH = $this->loadRecordHeader($stream, $pos); $pos += 8; + $readSuccess = false; if ($arrayRH['recVer'] == 0x00 && ($arrayRH['recType'] == 0xF007 || ($arrayRH['recType'] >= 0xF018 && $arrayRH['recType'] <= 0xF117))) { //@link : http://msdn.microsoft.com/en-us/library/dd950560(v=office.12).aspx if ($arrayRH['recType'] == 0xF007) { @@ -482,8 +481,7 @@ private function loadPicturesStream() $this->arrayPictures[] = $arrayRecord['picture']; } } - } else { - $readSuccess = false; + $readSuccess = true; } } while ($readSuccess === true); } @@ -678,11 +676,11 @@ public static function getInt4d($data, $pos) // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334 // Hacked by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems $or24 = ord($data[$pos + 3]); + + $ord24 = ($or24 & 127) << 24; if ($or24 >= 128) { // negative number $ord24 = -abs((256 - $or24) << 24); - } else { - $ord24 = ($or24 & 127) << 24; } return ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | $ord24; } @@ -826,37 +824,36 @@ private function readRecordDocumentContainer($stream, $pos) $fontCollection['recLen'] -= 8; if ($fontEntityAtom['recVer'] != 0x0 || $fontEntityAtom['recInstance'] > 128 || $fontEntityAtom['recType'] != self::RT_FONTENTITYATOM) { throw new \Exception('File PowerPoint 97 in error (Location : RTDocument > RT_Environment > RT_FontCollection > RT_FontEntityAtom).'); - } else { - $string = ''; - for ($inc = 0; $inc < 32; $inc++) { - $char = self::getInt2d($stream, $pos); - $pos += 2; - $fontCollection['recLen'] -= 2; - $string .= chr($char); - } - $this->arrayFonts[] = $string; - - // lfCharSet (1 byte) - $pos += 1; - $fontCollection['recLen'] -= 1; - - // fEmbedSubsetted (1 bit) - // unused (7 bits) - $pos += 1; - $fontCollection['recLen'] -= 1; - - // rasterFontType (1 bit) - // deviceFontType (1 bit) - // truetypeFontType (1 bit) - // fNoFontSubstitution (1 bit) - // reserved (4 bits) - $pos += 1; - $fontCollection['recLen'] -= 1; - - // lfPitchAndFamily (1 byte) - $pos += 1; - $fontCollection['recLen'] -= 1; } + $string = ''; + for ($inc = 0; $inc < 32; $inc++) { + $char = self::getInt2d($stream, $pos); + $pos += 2; + $fontCollection['recLen'] -= 2; + $string .= chr($char); + } + $this->arrayFonts[] = $string; + + // lfCharSet (1 byte) + $pos += 1; + $fontCollection['recLen'] -= 1; + + // fEmbedSubsetted (1 bit) + // unused (7 bits) + $pos += 1; + $fontCollection['recLen'] -= 1; + + // rasterFontType (1 bit) + // deviceFontType (1 bit) + // truetypeFontType (1 bit) + // fNoFontSubstitution (1 bit) + // reserved (4 bits) + $pos += 1; + $fontCollection['recLen'] -= 1; + + // lfPitchAndFamily (1 byte) + $pos += 1; + $fontCollection['recLen'] -= 1; $fontEmbedData1 = $this->loadRecordHeader($stream, $pos); if ($fontEmbedData1['recVer'] == 0x0 && $fontEmbedData1['recInstance'] >= 0x000 && $fontEmbedData1['recInstance'] <= 0x003 && $fontEmbedData1['recType'] == self::RT_FONTEMBEDDATABLOB) { @@ -889,7 +886,6 @@ private function readRecordDocumentContainer($stream, $pos) $pos += $fontEmbedData4['recLen']; $fontCollection['recLen'] -= $fontEmbedData4['recLen']; } - } while ($fontCollection['recLen'] > 0); } } @@ -1422,7 +1418,7 @@ private function readRecordOfficeArtSpContainer($stream, $pos) $drawingPib = $shpPrimaryOptions['pib']; if (isset($this->arrayPictures[$drawingPib - 1])) { $gdImage = imagecreatefromstring($this->arrayPictures[$drawingPib - 1]); - $arrayReturn['shape'] = new MemoryDrawing(); + $arrayReturn['shape'] = new Drawing\Gd(); $arrayReturn['shape']->setImageResource($gdImage); } } elseif (isset($shpPrimaryOptions['line']) && $shpPrimaryOptions['line']) { diff --git a/src/PhpPresentation/Reader/ReaderInterface.php b/src/PhpPresentation/Reader/ReaderInterface.php index 2a4157e7d..c5149e99c 100644 --- a/src/PhpPresentation/Reader/ReaderInterface.php +++ b/src/PhpPresentation/Reader/ReaderInterface.php @@ -34,6 +34,7 @@ public function canRead($pFilename); * Loads PhpPresentation from file * * @param string $pFilename + * @return \PhpOffice\PhpPresentation\PhpPresentation * @throws \Exception */ public function load($pFilename); diff --git a/src/PhpPresentation/Shape/AbstractDrawing.php b/src/PhpPresentation/Shape/AbstractGraphic.php similarity index 98% rename from src/PhpPresentation/Shape/AbstractDrawing.php rename to src/PhpPresentation/Shape/AbstractGraphic.php index 1a11119e9..2c0b8fba6 100644 --- a/src/PhpPresentation/Shape/AbstractDrawing.php +++ b/src/PhpPresentation/Shape/AbstractGraphic.php @@ -23,7 +23,7 @@ /** * Abstract drawing */ -abstract class AbstractDrawing extends AbstractShape implements ComparableInterface +abstract class AbstractGraphic extends AbstractShape implements ComparableInterface { /** * Image counter diff --git a/src/PhpPresentation/Shape/Chart.php b/src/PhpPresentation/Shape/Chart.php index 73a7ed690..a851801fb 100644 --- a/src/PhpPresentation/Shape/Chart.php +++ b/src/PhpPresentation/Shape/Chart.php @@ -26,7 +26,7 @@ /** * Chart element */ -class Chart extends AbstractDrawing implements ComparableInterface +class Chart extends AbstractGraphic implements ComparableInterface { /** * Title @@ -64,7 +64,7 @@ class Chart extends AbstractDrawing implements ComparableInterface private $includeSpreadsheet = false; /** - * Create a new \PhpOffice\PhpPresentation\Slide\MemoryDrawing + * Create a new Chart */ public function __construct() { diff --git a/src/PhpPresentation/Shape/Chart/Axis.php b/src/PhpPresentation/Shape/Chart/Axis.php index f8ec31122..4c1aa74e0 100644 --- a/src/PhpPresentation/Shape/Chart/Axis.php +++ b/src/PhpPresentation/Shape/Chart/Axis.php @@ -18,12 +18,16 @@ namespace PhpOffice\PhpPresentation\Shape\Chart; use PhpOffice\PhpPresentation\ComparableInterface; +use PhpOffice\PhpPresentation\Style\Font; /** * \PhpOffice\PhpPresentation\Shape\Chart\Axis */ class Axis implements ComparableInterface { + const AXIS_X = 'x'; + const AXIS_Y = 'y'; + /** * Title * @@ -38,6 +42,23 @@ class Axis implements ComparableInterface */ private $formatCode = ''; + /** + * Font + * + * @var \PhpOffice\PhpPresentation\Style\Font + */ + private $font; + + /** + * @var Gridlines + */ + protected $majorGridlines; + + /** + * @var Gridlines + */ + protected $minorGridlines; + /** * Create a new \PhpOffice\PhpPresentation\Shape\Chart\Axis instance * @@ -46,6 +67,7 @@ class Axis implements ComparableInterface public function __construct($title = 'Axis Title') { $this->title = $title; + $this->font = new Font(); } /** @@ -71,6 +93,29 @@ public function setTitle($value = 'Axis Title') return $this; } + /** + * Get font + * + * @return \PhpOffice\PhpPresentation\Style\Font + */ + public function getFont() + { + return $this->font; + } + + /** + * Set font + * + * @param \PhpOffice\PhpPresentation\Style\Font $pFont Font + * @throws \Exception + * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph + */ + public function setFont(Font $pFont = null) + { + $this->font = $pFont; + return $this; + } + /** * Get Format Code * @@ -94,6 +139,42 @@ public function setFormatCode($value = '') return $this; } + /** + * @return Gridlines + */ + public function getMajorGridlines() + { + return $this->majorGridlines; + } + + /** + * @param Gridlines $majorGridlines + * @return Axis + */ + public function setMajorGridlines(Gridlines $majorGridlines) + { + $this->majorGridlines = $majorGridlines; + return $this; + } + + /** + * @return Gridlines + */ + public function getMinorGridlines() + { + return $this->minorGridlines; + } + + /** + * @param Gridlines $minorGridlines + * @return Axis + */ + public function setMinorGridlines(Gridlines $minorGridlines) + { + $this->minorGridlines = $minorGridlines; + return $this; + } + /** * Get hash code * diff --git a/src/PhpPresentation/Shape/Chart/Gridlines.php b/src/PhpPresentation/Shape/Chart/Gridlines.php new file mode 100644 index 000000000..4040a5a4b --- /dev/null +++ b/src/PhpPresentation/Shape/Chart/Gridlines.php @@ -0,0 +1,36 @@ +outline = new Outline(); + } + + /** + * @return Outline + */ + public function getOutline() + { + return $this->outline; + } + + /** + * @param Outline $outline + * @return Gridlines + */ + public function setOutline(Outline $outline) + { + $this->outline = $outline; + return $this; + } +} diff --git a/src/PhpPresentation/Shape/Chart/Marker.php b/src/PhpPresentation/Shape/Chart/Marker.php new file mode 100644 index 000000000..736069775 --- /dev/null +++ b/src/PhpPresentation/Shape/Chart/Marker.php @@ -0,0 +1,96 @@ +symbol; + } + + /** + * @param string $symbol + * @return Marker + */ + public function setSymbol($symbol = self::SYMBOL_NONE) + { + $this->symbol = $symbol; + + return $this; + } + + /** + * @return int + */ + public function getSize() + { + return $this->size; + } + + /** + * @param int $size + * @return Marker + */ + public function setSize($size = 5) + { + $this->size = $size; + + return $this; + } +} diff --git a/src/PhpPresentation/Shape/Chart/PlotArea.php b/src/PhpPresentation/Shape/Chart/PlotArea.php index 7ad5e0791..cdabff69f 100644 --- a/src/PhpPresentation/Shape/Chart/PlotArea.php +++ b/src/PhpPresentation/Shape/Chart/PlotArea.php @@ -18,6 +18,7 @@ namespace PhpOffice\PhpPresentation\Shape\Chart; use PhpOffice\PhpPresentation\ComparableInterface; +use PhpOffice\PhpPresentation\Shape\Chart\Type\AbstractType; /** * \PhpOffice\PhpPresentation\Shape\Chart\PlotArea @@ -92,7 +93,7 @@ public function __clone() /** * Get type * - * @return \PhpOffice\PhpPresentation\Shape\Chart\AbstractType + * @return AbstractType * @throws \Exception */ public function getType() diff --git a/src/PhpPresentation/Shape/Chart/Series.php b/src/PhpPresentation/Shape/Chart/Series.php index 82f7c04c2..b8c725614 100644 --- a/src/PhpPresentation/Shape/Chart/Series.php +++ b/src/PhpPresentation/Shape/Chart/Series.php @@ -20,6 +20,7 @@ use PhpOffice\PhpPresentation\ComparableInterface; use PhpOffice\PhpPresentation\Style\Fill; use PhpOffice\PhpPresentation\Style\Font; +use PhpOffice\PhpPresentation\Style\Outline; /** * \PhpOffice\PhpPresentation\Shape\Chart\Series @@ -27,7 +28,7 @@ class Series implements ComparableInterface { /* Label positions */ - const LABEL_BESTFIT = 'bestFir'; + const LABEL_BESTFIT = 'bestFit'; const LABEL_BOTTOM = 'b'; const LABEL_CENTER = 'ctr'; const LABEL_INSIDEBASE = 'inBase'; @@ -38,11 +39,11 @@ class Series implements ComparableInterface const LABEL_TOP = 't'; /** - * Title + * DataPointFills (key/value) * - * @var string + * @var array */ - private $title = 'Series Title'; + private $dataPointFills = array(); /** * Data Label Number Format @@ -59,25 +60,28 @@ class Series implements ComparableInterface private $fill; /** - * Values (key/value) + * Font * - * @var array + * @var \PhpOffice\PhpPresentation\Style\Font */ - private $values = array(); + private $font; /** - * DataPointFills (key/value) + * Label position * - * @var array + * @var string */ - private $dataPointFills = array(); + private $labelPosition = 'ctr'; /** - * ShowSeriesName - * - * @var boolean + * @var Marker */ - private $showSeriesName = false; + protected $marker; + + /** + * @var Outline + */ + protected $outline; /** * ShowCategoryName @@ -87,11 +91,11 @@ class Series implements ComparableInterface private $showCategoryName = false; /** - * ShowValue + * ShowLeaderLines * * @var boolean */ - private $showValue = true; + private $showLeaderLines = true; /** * ShowPercentage @@ -101,25 +105,32 @@ class Series implements ComparableInterface private $showPercentage = false; /** - * ShowLeaderLines + * ShowSeriesName * * @var boolean */ - private $showLeaderLines = true; + private $showSeriesName = false; /** - * Font + * ShowValue * - * @var \PhpOffice\PhpPresentation\Style\Font + * @var boolean */ - private $font; + private $showValue = true; /** - * Label position + * Title * * @var string */ - private $labelPosition = 'ctr'; + private $title = 'Series Title'; + + /** + * Values (key/value) + * + * @var array + */ + private $values = array(); /** * Hash index @@ -142,6 +153,7 @@ public function __construct($title = 'Series Title', $values = array()) $this->font->setSize(9); $this->title = $title; $this->values = $values; + $this->marker = new Marker(); } /** @@ -239,7 +251,7 @@ public function getDataPointFill($dataPointIndex) /** * Get DataPointFills * - * @return array + * @return Fill[] */ public function getDataPointFills() { @@ -445,6 +457,42 @@ public function setLabelPosition($value) return $this; } + /** + * @return Marker + */ + public function getMarker() + { + return $this->marker; + } + + /** + * @param Marker $marker + * @return \PhpOffice\PhpPresentation\Shape\Chart\Series + */ + public function setMarker(Marker $marker) + { + $this->marker = $marker; + return $this; + } + + /** + * @return Outline + */ + public function getOutline() + { + return $this->outline; + } + + /** + * @param Outline $outline + * @return \PhpOffice\PhpPresentation\Shape\Chart\Series + */ + public function setOutline(Outline $outline) + { + $this->outline = $outline; + return $this; + } + /** * Get hash code * @@ -475,10 +523,24 @@ public function getHashIndex() * while doing a write of a workbook and when changes are not allowed. * * @param string $value Hash index + * @return \PhpOffice\PhpPresentation\Shape\Chart\Series */ public function setHashIndex($value) { $this->hashIndex = $value; return $this; } + + /** + * @return mixed + * @link http://php.net/manual/en/language.oop5.cloning.php + */ + public function __clone() + { + $this->font = clone $this->font; + $this->marker = clone $this->marker; + if (is_object($this->outline)) { + $this->outline = clone $this->outline; + } + } } diff --git a/src/PhpPresentation/Shape/Chart/Type/AbstractType.php b/src/PhpPresentation/Shape/Chart/Type/AbstractType.php index 13ee248b9..9e91520b1 100644 --- a/src/PhpPresentation/Shape/Chart/Type/AbstractType.php +++ b/src/PhpPresentation/Shape/Chart/Type/AbstractType.php @@ -111,26 +111,59 @@ public function addSeries(Series $value) $this->data[] = $value; return $this; } - + /** - * Get Data + * Get Series * * @return \PhpOffice\PhpPresentation\Shape\Chart\Series[] */ - public function getData() + public function getSeries() { return $this->data; } - + /** - * Set Data + * Set Series * * @param array $value Array of \PhpOffice\PhpPresentation\Shape\Chart\Series * @return self */ - public function setData($value = array()) + public function setSeries($value = array()) { $this->data = $value; return $this; } + + /** + * Get Data + * + * @deprecated getSeries + */ + public function getData() + { + return $this->getSeries(); + } + + /** + * Set Data + * + * @deprecated setSeries + */ + public function setData($value = array()) + { + return $this->setSeries($value); + } + + /** + * @return mixed + * @link http://php.net/manual/en/language.oop5.cloning.php + */ + public function __clone() + { + $arrayClone = array(); + foreach ($this->data as $itemSeries) { + $arrayClone[] = clone $itemSeries; + } + $this->data = $arrayClone; + } } diff --git a/src/PhpPresentation/Shape/Chart/Type/AbstractTypeBar.php b/src/PhpPresentation/Shape/Chart/Type/AbstractTypeBar.php index 5b066ef14..e4015b3a3 100644 --- a/src/PhpPresentation/Shape/Chart/Type/AbstractTypeBar.php +++ b/src/PhpPresentation/Shape/Chart/Type/AbstractTypeBar.php @@ -100,7 +100,7 @@ public function getBarGrouping() public function getHashCode() { $hash = ''; - foreach ($this->getData() as $series) { + foreach ($this->getSeries() as $series) { $hash .= $series->getHashCode(); } return $hash; diff --git a/src/PhpPresentation/Shape/Chart/Type/AbstractTypePie.php b/src/PhpPresentation/Shape/Chart/Type/AbstractTypePie.php index b945b9cff..f055fe3d4 100644 --- a/src/PhpPresentation/Shape/Chart/Type/AbstractTypePie.php +++ b/src/PhpPresentation/Shape/Chart/Type/AbstractTypePie.php @@ -68,7 +68,7 @@ public function getExplosion() public function getHashCode() { $hash = ''; - foreach ($this->getData() as $series) { + foreach ($this->getSeries() as $series) { $hash .= $series->getHashCode(); } return $hash; diff --git a/src/PhpPresentation/Shape/Chart/Type/Area.php b/src/PhpPresentation/Shape/Chart/Type/Area.php index 741ef89f8..fdfc5aaed 100644 --- a/src/PhpPresentation/Shape/Chart/Type/Area.php +++ b/src/PhpPresentation/Shape/Chart/Type/Area.php @@ -32,7 +32,7 @@ class Area extends AbstractType implements ComparableInterface public function getHashCode() { $hash = ''; - foreach ($this->getData() as $series) { + foreach ($this->getSeries() as $series) { $hash .= $series->getHashCode(); } return md5($hash . __CLASS__); diff --git a/src/PhpPresentation/Shape/Chart/Type/Line.php b/src/PhpPresentation/Shape/Chart/Type/Line.php index 872cb1f02..9be7a7852 100644 --- a/src/PhpPresentation/Shape/Chart/Type/Line.php +++ b/src/PhpPresentation/Shape/Chart/Type/Line.php @@ -32,7 +32,7 @@ class Line extends AbstractType implements ComparableInterface public function getHashCode() { $hash = ''; - foreach ($this->getData() as $series) { + foreach ($this->getSeries() as $series) { $hash .= $series->getHashCode(); } return md5($hash . __CLASS__); diff --git a/src/PhpPresentation/Shape/Chart/Type/Scatter.php b/src/PhpPresentation/Shape/Chart/Type/Scatter.php index ec12cbf35..ba1fa3b9b 100644 --- a/src/PhpPresentation/Shape/Chart/Type/Scatter.php +++ b/src/PhpPresentation/Shape/Chart/Type/Scatter.php @@ -32,7 +32,7 @@ class Scatter extends AbstractType implements ComparableInterface public function getHashCode() { $hash = ''; - foreach ($this->getData() as $series) { + foreach ($this->getSeries() as $series) { $hash .= $series->getHashCode(); } return md5($hash . __CLASS__); diff --git a/src/PhpPresentation/Shape/Comment.php b/src/PhpPresentation/Shape/Comment.php new file mode 100644 index 000000000..96f2fb601 --- /dev/null +++ b/src/PhpPresentation/Shape/Comment.php @@ -0,0 +1,145 @@ +setDate(time()); + } + + /** + * @return Author + */ + public function getAuthor() + { + return $this->author; + } + + /** + * @param Author $author + * @return Comment + */ + public function setAuthor(Author $author) + { + $this->author = $author; + return $this; + } + + /** + * @return int + */ + public function getDate() + { + return $this->dtComment; + } + + /** + * @param int $dtComment timestamp of the comment + * @return Comment + */ + public function setDate($dtComment) + { + $this->dtComment = (int)$dtComment; + return $this; + } + + /** + * @return string + */ + public function getText() + { + return $this->text; + } + + /** + * @param string $text + * @return Comment + */ + public function setText($text = '') + { + $this->text = $text; + return $this; + } + + /** + * Comment has not height + * + * @return null + */ + public function getHeight() + { + return null; + } + + /** + * Set Height + * + * @param int $pValue + * @return self + */ + public function setHeight($pValue = 0) + { + return $this; + } + + /** + * Comment has not width + * + * @return null + */ + public function getWidth() + { + return null; + } + + /** + * Set Width + * + * @param int $pValue + * @return self + */ + public function setWidth($pValue = 0) + { + return $this; + } +} diff --git a/src/PhpPresentation/Shape/Comment/Author.php b/src/PhpPresentation/Shape/Comment/Author.php new file mode 100644 index 000000000..3c0ba2abf --- /dev/null +++ b/src/PhpPresentation/Shape/Comment/Author.php @@ -0,0 +1,85 @@ +idxAuthor; + } + + /** + * @param int $idxAuthor + * @return Author + */ + public function setIndex($idxAuthor) + { + $this->idxAuthor = (int) $idxAuthor; + return $this; + } + + /** + * @return mixed + */ + public function getInitials() + { + return $this->initials; + } + + /** + * @param mixed $initials + * @return Author + */ + public function setInitials($initials) + { + $this->initials = $initials; + return $this; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $name + * @return Author + */ + public function setName($name) + { + $this->name = $name; + return $this; + } + + /** + * Get hash code + * + * @return string Hash code + */ + public function getHashCode() + { + return md5($this->getInitials() . $this->getName() . __CLASS__); + } +} diff --git a/src/PhpPresentation/Shape/Drawing.php b/src/PhpPresentation/Shape/Drawing.php index 91651bf23..30a05b537 100644 --- a/src/PhpPresentation/Shape/Drawing.php +++ b/src/PhpPresentation/Shape/Drawing.php @@ -17,109 +17,12 @@ namespace PhpOffice\PhpPresentation\Shape; -use PhpOffice\PhpPresentation\ComparableInterface; +use PhpOffice\PhpPresentation\Shape\Drawing\File; /** * Drawing element + * @deprecated Drawing\File */ -class Drawing extends AbstractDrawing implements ComparableInterface +class Drawing extends File { - /** - * Path - * - * @var string - */ - private $path; - - /** - * Create a new \PhpOffice\PhpPresentation\Slide\Drawing - */ - public function __construct() - { - // Initialise values - $this->path = ''; - - // Initialize parent - parent::__construct(); - } - - /** - * Get Filename - * - * @return string - */ - public function getFilename() - { - return basename($this->path); - } - - /** - * Get indexed filename (using image index) - * - * @return string - */ - public function getIndexedFilename() - { - return str_replace('.' . $this->getExtension(), '', $this->getFilename()) . $this->getImageIndex() . '.' . $this->getExtension(); - } - - /** - * Get Extension - * - * @return string - */ - public function getExtension() - { - $exploded = explode(".", basename($this->path)); - - return $exploded[count($exploded) - 1]; - } - - /** - * Get Path - * - * @return string - */ - public function getPath() - { - return $this->path; - } - - /** - * Set Path - * - * @param string $pValue File path - * @param boolean $pVerifyFile Verify file - * @throws \Exception - * @return \PhpOffice\PhpPresentation\Shape\Drawing - */ - public function setPath($pValue = '', $pVerifyFile = true) - { - if ($pVerifyFile) { - if (file_exists($pValue)) { - $this->path = $pValue; - - if ($this->width == 0 && $this->height == 0) { - // Get width/height - list($this->width, $this->height) = getimagesize($pValue); - } - } else { - throw new \Exception("File $pValue not found!"); - } - } else { - $this->path = $pValue; - } - - return $this; - } - - /** - * Get hash code - * - * @return string Hash code - */ - public function getHashCode() - { - return md5($this->path . parent::getHashCode() . __CLASS__); - } } diff --git a/src/PhpPresentation/Shape/Drawing/AbstractDrawingAdapter.php b/src/PhpPresentation/Shape/Drawing/AbstractDrawingAdapter.php new file mode 100644 index 000000000..1301e2571 --- /dev/null +++ b/src/PhpPresentation/Shape/Drawing/AbstractDrawingAdapter.php @@ -0,0 +1,28 @@ + 'jpg', + ); + + /** + * Base64 constructor. + */ + public function __construct() + { + parent::__construct(); + $this->uniqueName = md5(rand(0, 9999) . time() . rand(0, 9999)); + } + + /** + * @return mixed + */ + public function getData() + { + return $this->data; + } + + /** + * @param mixed $data + * @return Base64 + */ + public function setData($data) + { + $this->data = $data; + return $this; + } + + /** + * @return string + */ + public function getContents() + { + list(, $imageContents) = explode(';', $this->getData()); + list(, $imageContents) = explode(',', $imageContents); + return base64_decode($imageContents); + } + + /** + * @return string + */ + public function getExtension() + { + list($data, ) = explode(';', $this->getData()); + list(, $mime) = explode(':', $data); + + if (!array_key_exists($mime, $this->arrayMimeExtension)) { + throw new \Exception('Type Mime not found : "'.$mime.'"'); + } + return $this->arrayMimeExtension[$mime]; + } + + /** + * @return string + */ + public function getIndexedFilename() + { + return $this->uniqueName . $this->getImageIndex() . '.' . $this->getExtension(); + } + + /** + * @return string + */ + public function getMimeType() + { + $sImage = $this->getContents(); + if (!function_exists('getimagesizefromstring')) { + $uri = 'data://application/octet-stream;base64,' . base64_encode($sImage); + $image = getimagesize($uri); + } else { + $image = getimagesizefromstring($sImage); + } + return image_type_to_mime_type($image[2]); + } +} diff --git a/src/PhpPresentation/Shape/Drawing/File.php b/src/PhpPresentation/Shape/Drawing/File.php new file mode 100644 index 000000000..908b6b52f --- /dev/null +++ b/src/PhpPresentation/Shape/Drawing/File.php @@ -0,0 +1,88 @@ +path; + } + + /** + * Set Path + * + * @param string $pValue File path + * @param boolean $pVerifyFile Verify file + * @throws \Exception + * @return \PhpOffice\PhpPresentation\Shape\Drawing + */ + public function setPath($pValue = '', $pVerifyFile = true) + { + if ($pVerifyFile) { + if (!file_exists($pValue)) { + throw new \Exception("File $pValue not found!"); + } + } + $this->path = $pValue; + + if ($pVerifyFile) { + if ($this->width == 0 && $this->height == 0) { + list($this->width, $this->height) = getimagesize($this->getPath()); + } + } + + return $this; + } + + /** + * @return string + */ + public function getContents() + { + return file_get_contents($this->getPath()); + } + + + /** + * @return string + */ + public function getExtension() + { + return pathinfo($this->getPath(), PATHINFO_EXTENSION); + } + + /** + * @return string + */ + public function getMimeType() + { + if (!file_exists($this->getPath())) { + throw new \Exception('File '.$this->getPath().' does not exist'); + } + $image = getimagesize($this->getPath()); + return image_type_to_mime_type($image[2]); + } + + /** + * @return string + */ + public function getIndexedFilename() + { + $output = str_replace('.' . $this->getExtension(), '', pathinfo($this->getPath(), PATHINFO_FILENAME)); + $output .= $this->getImageIndex(); + $output .= '.'.$this->getExtension(); + $output = str_replace(' ', '_', $output); + return $output; + } +} diff --git a/src/PhpPresentation/Shape/Drawing/Gd.php b/src/PhpPresentation/Shape/Drawing/Gd.php new file mode 100644 index 000000000..f69b345ea --- /dev/null +++ b/src/PhpPresentation/Shape/Drawing/Gd.php @@ -0,0 +1,159 @@ +uniqueName = md5(rand(0, 9999) . time() . rand(0, 9999)); + } + + /** + * Get image resource + * + * @return resource + */ + public function getImageResource() + { + return $this->imageResource; + } + + /** + * Set image resource + * + * @param $value resource + * @return $this + */ + public function setImageResource($value = null) + { + $this->imageResource = $value; + + if (!is_null($this->imageResource)) { + // Get width/height + $this->width = imagesx($this->imageResource); + $this->height = imagesy($this->imageResource); + } + + return $this; + } + + /** + * Get rendering function + * + * @return string + */ + public function getRenderingFunction() + { + return $this->renderingFunction; + } + + /** + * Set rendering function + * + * @param string $value + * @return $this + */ + public function setRenderingFunction($value = self::RENDERING_DEFAULT) + { + $this->renderingFunction = $value; + return $this; + } + + /** + * Get mime type + * + * @return string + */ + public function getMimeType() + { + return $this->mimeType; + } + + /** + * Set mime type + * + * @param string $value + * @return $this + */ + public function setMimeType($value = self::MIMETYPE_DEFAULT) + { + $this->mimeType = $value; + return $this; + } + + /** + * @return string + */ + public function getContents() + { + ob_start(); + call_user_func($this->getRenderingFunction(), $this->getImageResource()); + $imageContents = ob_get_contents(); + ob_end_clean(); + return $imageContents; + } + + /** + * @return string + */ + public function getExtension() + { + $extension = strtolower($this->getMimeType()); + $extension = explode('/', $extension); + $extension = $extension[1]; + return $extension; + } + + /** + * @return string + */ + public function getIndexedFilename() + { + return $this->uniqueName . $this->getImageIndex() . '.' . $this->getExtension(); + } +} diff --git a/src/PhpPresentation/Shape/Drawing/ZipFile.php b/src/PhpPresentation/Shape/Drawing/ZipFile.php new file mode 100644 index 000000000..3b8e5867b --- /dev/null +++ b/src/PhpPresentation/Shape/Drawing/ZipFile.php @@ -0,0 +1,107 @@ +path; + } + + /** + * Set Path + * + * @param string $pValue File path + * @return \PhpOffice\PhpPresentation\Shape\Drawing + */ + public function setPath($pValue = '') + { + $this->path = $pValue; + return $this; + } + + /** + * @return string + */ + public function getContents() + { + if (!CommonFile::fileExists($this->getZipFileOut())) { + throw new \Exception('File '.$this->getZipFileOut().' does not exist'); + } + + $imageZip = new \ZipArchive(); + $imageZip->open($this->getZipFileOut()); + $imageContents = $imageZip->getFromName($this->getZipFileIn()); + $imageZip->close(); + unset($imageZip); + return $imageContents; + } + + + /** + * @return string + */ + public function getExtension() + { + return pathinfo($this->getZipFileIn(), PATHINFO_EXTENSION); + } + + /** + * @return string + */ + public function getMimeType() + { + if (!CommonFile::fileExists($this->getZipFileOut())) { + throw new \Exception('File '.$this->getZipFileOut().' does not exist'); + } + $oArchive = new \ZipArchive(); + $oArchive->open($this->getZipFileOut()); + if (!function_exists('getimagesizefromstring')) { + $uri = 'data://application/octet-stream;base64,' . base64_encode($oArchive->getFromName($this->getZipFileIn())); + $image = getimagesize($uri); + } else { + $image = getimagesizefromstring($oArchive->getFromName($this->getZipFileIn())); + } + return image_type_to_mime_type($image[2]); + } + + /** + * @return string + */ + public function getIndexedFilename() + { + $output = pathinfo($this->getZipFileIn(), PATHINFO_FILENAME); + $output = str_replace('.' . $this->getExtension(), '', $output); + $output .= $this->getImageIndex(); + $output .= '.'.$this->getExtension(); + $output = str_replace(' ', '_', $output); + return $output; + } + + protected function getZipFileOut() + { + $path = str_replace('zip://', '', $this->getPath()); + $path = explode('#', $path); + return empty($path[0]) ? '' : $path[0]; + } + + protected function getZipFileIn() + { + $path = str_replace('zip://', '', $this->getPath()); + $path = explode('#', $path); + return empty($path[1]) ? '' : $path[1]; + } +} diff --git a/src/PhpPresentation/Shape/Group.php b/src/PhpPresentation/Shape/Group.php index 26f734b61..6f894e95f 100644 --- a/src/PhpPresentation/Shape/Group.php +++ b/src/PhpPresentation/Shape/Group.php @@ -21,7 +21,6 @@ use PhpOffice\PhpPresentation\GeometryCalculator; use PHPOffice\PhpPresentation\ShapeContainerInterface; use PhpOffice\PhpPresentation\Shape\Drawing; -use PhpOffice\PhpPresentation\Shape\Line; use PhpOffice\PhpPresentation\Shape\RichText; use PhpOffice\PhpPresentation\Shape\Table; @@ -63,7 +62,7 @@ public function __construct() /** * Get collection of shapes * - * @return \ArrayObject|\PhpOffice\PhpPresentation\AbstractShape[] + * @return \ArrayObject */ public function getShapeCollection() { @@ -237,11 +236,11 @@ public function createChartShape() /** * Create drawing shape * - * @return \PhpOffice\PhpPresentation\Shape\Drawing + * @return \PhpOffice\PhpPresentation\Shape\Drawing\File */ public function createDrawingShape() { - $shape = new Drawing(); + $shape = new Drawing\File(); $this->addShape($shape); return $shape; diff --git a/src/PhpPresentation/Shape/Line.php b/src/PhpPresentation/Shape/Line.php index 2fc16cdcd..e9a7a074c 100644 --- a/src/PhpPresentation/Shape/Line.php +++ b/src/PhpPresentation/Shape/Line.php @@ -43,7 +43,6 @@ public function __construct($fromX, $fromY, $toX, $toY) $this->setOffsetY($fromY); $this->setWidth($toX - $fromX); $this->setHeight($toY - $fromY); - } /** diff --git a/src/PhpPresentation/Shape/Media.php b/src/PhpPresentation/Shape/Media.php new file mode 100644 index 000000000..64a98b72c --- /dev/null +++ b/src/PhpPresentation/Shape/Media.php @@ -0,0 +1,49 @@ +getExtension())) { + case 'mp4': + $mimetype = 'video/mp4'; + break; + case 'ogv': + $mimetype = 'video/ogg'; + break; + case 'wmv': + $mimetype = 'video/x-ms-wmv'; + break; + default: + $mimetype = 'application/octet-stream'; + } + return $mimetype; + } +} diff --git a/src/PhpPresentation/Shape/MemoryDrawing.php b/src/PhpPresentation/Shape/MemoryDrawing.php index b1ca2546a..e4b5c7a17 100644 --- a/src/PhpPresentation/Shape/MemoryDrawing.php +++ b/src/PhpPresentation/Shape/MemoryDrawing.php @@ -17,170 +17,12 @@ namespace PhpOffice\PhpPresentation\Shape; -use PhpOffice\PhpPresentation\ComparableInterface; +use PhpOffice\PhpPresentation\Shape\Drawing\Gd; /** * Memory drawing shape + * @deprecated Drawing\Gd */ -class MemoryDrawing extends AbstractDrawing implements ComparableInterface +class MemoryDrawing extends Gd { - /* Rendering functions */ - const RENDERING_DEFAULT = 'imagepng'; - const RENDERING_PNG = 'imagepng'; - const RENDERING_GIF = 'imagegif'; - const RENDERING_JPEG = 'imagejpeg'; - - /* MIME types */ - const MIMETYPE_DEFAULT = 'image/png'; - const MIMETYPE_PNG = 'image/png'; - const MIMETYPE_GIF = 'image/gif'; - const MIMETYPE_JPEG = 'image/jpeg'; - - /** - * Image resource - * - * @var resource - */ - private $imageResource; - - /** - * Rendering function - * - * @var string - */ - private $renderingFunction; - - /** - * Mime type - * - * @var string - */ - private $mimeType; - - /** - * Unique name - * - * @var string - */ - private $uniqueName; - - /** - * Create a new \PhpOffice\PhpPresentation\Slide\MemoryDrawing - */ - public function __construct() - { - // Initialise values - $this->setImageResource(null); - $this->setRenderingFunction(self::RENDERING_DEFAULT); - $this->setMimeType(self::MIMETYPE_DEFAULT); - $this->uniqueName = md5(rand(0, 9999) . time() . rand(0, 9999)); - - // Initialize parent - parent::__construct(); - } - - /** - * Get image resource - * - * @return resource - */ - public function getImageResource() - { - return $this->imageResource; - } - - /** - * Set image resource - * - * @param $value resource - * @return \PhpOffice\PhpPresentation\Shape\MemoryDrawing - */ - public function setImageResource($value = null) - { - $this->imageResource = $value; - - if (!is_null($this->imageResource)) { - // Get width/height - $this->width = imagesx($this->imageResource); - $this->height = imagesy($this->imageResource); - } - - return $this; - } - - /** - * Get rendering function - * - * @return string - */ - public function getRenderingFunction() - { - return $this->renderingFunction; - } - - /** - * Set rendering function - * - * @param string $value - * @return \PhpOffice\PhpPresentation\Shape\MemoryDrawing - */ - public function setRenderingFunction($value = self::RENDERING_DEFAULT) - { - $this->renderingFunction = $value; - - return $this; - } - - /** - * Get mime type - * - * @return string - */ - public function getMimeType() - { - return $this->mimeType; - } - - /** - * Set mime type - * - * @param string $value - * @return \PhpOffice\PhpPresentation\Shape\MemoryDrawing - */ - public function setMimeType($value = self::MIMETYPE_DEFAULT) - { - $this->mimeType = $value; - - return $this; - } - - /** - * Get indexed filename (using image index) - * - * @return string - */ - public function getIndexedFilename() - { - return $this->uniqueName . $this->getImageIndex() . '.' . $this->getExtension(); - } - - /** - * Get extension - */ - public function getExtension() - { - $extension = strtolower($this->getMimeType()); - $extension = explode('/', $extension); - return $extension[1]; - } - - /** - * Get hash code - * - * @return string Hash code - */ - public function getHashCode() - { - return md5($this->renderingFunction . $this->mimeType . $this->uniqueName . parent::getHashCode() . __CLASS__); - } } diff --git a/src/PhpPresentation/Shape/Placeholder.php b/src/PhpPresentation/Shape/Placeholder.php new file mode 100644 index 000000000..bacd76c44 --- /dev/null +++ b/src/PhpPresentation/Shape/Placeholder.php @@ -0,0 +1,94 @@ +type = $type; + return $this; + } + + /** + * @return mixed + */ + public function getType() + { + return $this->type; + } + + /** + * @param mixed $type + * @return string + */ + public function setType($type) + { + $this->type = $type; + return $this; + } + + /** + * @return int + */ + public function getIdx() + { + return $this->idx; + } + + /** + * @param int $idx + */ + public function setIdx($idx) + { + $this->idx = $idx; + } +} diff --git a/src/PhpPresentation/Shape/RichText.php b/src/PhpPresentation/Shape/RichText.php index 43ec1a681..9782aaae8 100644 --- a/src/PhpPresentation/Shape/RichText.php +++ b/src/PhpPresentation/Shape/RichText.php @@ -351,13 +351,12 @@ public function getParagraphs() */ public function setParagraphs($paragraphs = null) { - if (is_array($paragraphs)) { - $this->richTextParagraphs = $paragraphs; - $this->activeParagraph = count($this->richTextParagraphs) - 1; - } else { + if (!is_array($paragraphs)) { throw new \Exception("Invalid \PhpOffice\PhpPresentation\Shape\RichText\Paragraph[] array passed."); } + $this->richTextParagraphs = $paragraphs; + $this->activeParagraph = count($this->richTextParagraphs) - 1; return $this; } diff --git a/src/PhpPresentation/Shape/RichText/BreakElement.php b/src/PhpPresentation/Shape/RichText/BreakElement.php index 4b5d37367..dbd5bb7c3 100644 --- a/src/PhpPresentation/Shape/RichText/BreakElement.php +++ b/src/PhpPresentation/Shape/RichText/BreakElement.php @@ -60,6 +60,27 @@ public function getFont() return null; } + /** + * Set language + * + * @param $lang + * @return \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface + */ + public function setLanguage($lang) + { + return $this; + } + + /** + * Get language + * + * @return string Language + */ + public function getLanguage() + { + return null; + } + /** * Get hash code * diff --git a/src/PhpPresentation/Shape/RichText/Paragraph.php b/src/PhpPresentation/Shape/RichText/Paragraph.php index 666b98154..aa2bf79d8 100644 --- a/src/PhpPresentation/Shape/RichText/Paragraph.php +++ b/src/PhpPresentation/Shape/RichText/Paragraph.php @@ -55,6 +55,11 @@ class Paragraph implements ComparableInterface */ private $bulletStyle; + /** + * @var integer + */ + private $lineSpacing = 100; + /** * Hash index * @@ -69,9 +74,9 @@ public function __construct() { // Initialise variables $this->richTextElements = array(); - $this->alignment = new Alignment(); - $this->font = new Font(); - $this->bulletStyle = new Bullet(); + $this->alignment = new Alignment(); + $this->font = new Font(); + $this->bulletStyle = new Bullet(); } /** @@ -87,7 +92,7 @@ public function getAlignment() /** * Set alignment * - * @param \PhpOffice\PhpPresentation\Style\Alignment $alignment + * @param \PhpOffice\PhpPresentation\Style\Alignment $alignment * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph */ public function setAlignment(Alignment $alignment) @@ -110,7 +115,7 @@ public function getFont() /** * Set font * - * @param \PhpOffice\PhpPresentation\Style\Font $pFont Font + * @param \PhpOffice\PhpPresentation\Style\Font $pFont Font * @throws \Exception * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph */ @@ -134,7 +139,7 @@ public function getBulletStyle() /** * Set bullet style * - * @param \PhpOffice\PhpPresentation\Style\Bullet $style + * @param \PhpOffice\PhpPresentation\Style\Bullet $style * @throws \Exception * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph */ @@ -146,32 +151,32 @@ public function setBulletStyle(Bullet $style = null) } /** - * Add text + * Create text (can not be formatted !) * - * @param \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface $pText Rich text element + * @param string $pText Text + * @return \PhpOffice\PhpPresentation\Shape\RichText\TextElement * @throws \Exception - * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph */ - public function addText(TextElementInterface $pText = null) + public function createText($pText = '') { - $this->richTextElements[] = $pText; + $objText = new TextElement($pText); + $this->addText($objText); - return $this; + return $objText; } /** - * Create text (can not be formatted !) + * Add text * - * @param string $pText Text - * @return \PhpOffice\PhpPresentation\Shape\RichText\TextElement + * @param \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface $pText Rich text element * @throws \Exception + * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph */ - public function createText($pText = '') + public function addText(TextElementInterface $pText = null) { - $objText = new TextElement($pText); - $this->addText($objText); + $this->richTextElements[] = $pText; - return $objText; + return $this; } /** @@ -191,7 +196,7 @@ public function createBreak() /** * Create text run (can be formatted) * - * @param string $pText Text + * @param string $pText Text * @return \PhpOffice\PhpPresentation\Shape\RichText\Run * @throws \Exception */ @@ -204,6 +209,16 @@ public function createTextRun($pText = '') return $objText; } + /** + * Convert to string + * + * @return string + */ + public function __toString() + { + return $this->getPlainText(); + } + /** * Get plain text * @@ -225,16 +240,6 @@ public function getPlainText() return $returnValue; } - /** - * Convert to string - * - * @return string - */ - public function __toString() - { - return $this->getPlainText(); - } - /** * Get Rich Text elements * @@ -303,4 +308,22 @@ public function setHashIndex($value) { $this->hashIndex = $value; } + + /** + * @return int + */ + public function getLineSpacing() + { + return $this->lineSpacing; + } + + /** + * @param int $lineSpacing + * @return Paragraph + */ + public function setLineSpacing($lineSpacing) + { + $this->lineSpacing = $lineSpacing; + return $this; + } } diff --git a/src/PhpPresentation/Shape/RichText/TextElement.php b/src/PhpPresentation/Shape/RichText/TextElement.php index 163d3fe36..2cdbd499b 100644 --- a/src/PhpPresentation/Shape/RichText/TextElement.php +++ b/src/PhpPresentation/Shape/RichText/TextElement.php @@ -31,6 +31,11 @@ class TextElement implements TextElementInterface */ private $text; + /** + * @var string + */ + protected $language; + /** * Hyperlink * @@ -120,6 +125,26 @@ public function setHyperlink(Hyperlink $pHyperlink = null) return $this; } + /** + * Get language + * @return string + */ + public function getLanguage() + { + return $this->language; + } + + /** + * Set language + * @param string $language + * @return TextElement + */ + public function setLanguage($language) + { + $this->language = $language; + return $this; + } + /** * Get hash code * diff --git a/src/PhpPresentation/Shape/RichText/TextElementInterface.php b/src/PhpPresentation/Shape/RichText/TextElementInterface.php index 472b8fd04..98302447a 100644 --- a/src/PhpPresentation/Shape/RichText/TextElementInterface.php +++ b/src/PhpPresentation/Shape/RichText/TextElementInterface.php @@ -44,6 +44,17 @@ public function setText($pText = ''); */ public function getFont(); + /** + * @return string Language + */ + public function getLanguage(); + + /** + * @param string $lang + * @return \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface + */ + public function setLanguage($lang); + /** * Get hash code * diff --git a/src/PhpPresentation/Shape/Table.php b/src/PhpPresentation/Shape/Table.php index 0e056e6a7..5786a2918 100644 --- a/src/PhpPresentation/Shape/Table.php +++ b/src/PhpPresentation/Shape/Table.php @@ -23,7 +23,7 @@ /** * Table shape */ -class Table extends AbstractDrawing implements ComparableInterface +class Table extends AbstractGraphic implements ComparableInterface { /** * Rows diff --git a/src/PhpPresentation/Slide.php b/src/PhpPresentation/Slide.php index 76ea01944..d5b331d4d 100644 --- a/src/PhpPresentation/Slide.php +++ b/src/PhpPresentation/Slide.php @@ -17,48 +17,28 @@ namespace PhpOffice\PhpPresentation; -use PhpOffice\PhpPresentation\GeometryCalculator; use PhpOffice\PhpPresentation\Shape\Chart; -use PhpOffice\PhpPresentation\Shape\Drawing; -use PhpOffice\PhpPresentation\Shape\Group; -use PhpOffice\PhpPresentation\Shape\Line; use PhpOffice\PhpPresentation\Shape\RichText; use PhpOffice\PhpPresentation\Shape\Table; -use PhpOffice\PhpPresentation\Slide\AbstractBackground; -use PhpOffice\PhpPresentation\Slide\Layout; +use PhpOffice\PhpPresentation\Slide\AbstractSlide; use PhpOffice\PhpPresentation\Slide\Note; -use PhpOffice\PhpPresentation\Slide\Transition; +use PhpOffice\PhpPresentation\Slide\SlideLayout; /** * Slide class */ -class Slide implements ComparableInterface, ShapeContainerInterface +class Slide extends AbstractSlide implements ComparableInterface, ShapeContainerInterface { /** - * Parent presentation - * - * @var PhpPresentation - */ - private $parent; - - /** - * Collection of shapes - * - * @var \ArrayObject|\PhpOffice\PhpPresentation\AbstractShape[] - */ - private $shapeCollection = null; - - /** - * Slide identifier - * - * @var string + * The slide is shown in presentation + * @var bool */ - private $identifier; + protected $isVisible = true; /** * Slide layout * - * @var string + * @var SlideLayout */ private $slideLayout; @@ -74,46 +54,12 @@ class Slide implements ComparableInterface, ShapeContainerInterface * @var \PhpOffice\PhpPresentation\Slide\Note */ private $slideNote; + /** * - * @var \PhpOffice\PhpPresentation\Slide\Transition - */ - private $slideTransition; - - /** - * Hash index - * - * @var string - */ - private $hashIndex; - - /** - * Offset X - * - * @var int - */ - protected $offsetX; - - /** - * Offset Y - * - * @var int - */ - protected $offsetY; - - /** - * Extent X - * - * @var int - */ - protected $extentX; - - /** - * Extent Y - * - * @var int + * @var \PhpOffice\PhpPresentation\Slide\Animation[] */ - protected $extentY; + protected $animations = array(); /** * Name of the title @@ -122,13 +68,6 @@ class Slide implements ComparableInterface, ShapeContainerInterface */ protected $name; - /** - * Background of the slide - * - * @var AbstractBackground - */ - protected $background; - /** * Create a new slide * @@ -138,150 +77,16 @@ public function __construct(PhpPresentation $pParent = null) { // Set parent $this->parent = $pParent; - - $this->slideLayout = Slide\Layout::BLANK; - // Shape collection $this->shapeCollection = new \ArrayObject(); - // Set identifier $this->identifier = md5(rand(0, 9999) . time()); } - /** - * Get collection of shapes - * - * @return \ArrayObject|\PhpOffice\PhpPresentation\AbstractShape[] - */ - public function getShapeCollection() - { - return $this->shapeCollection; - } - - /** - * Add shape to slide - * - * @param \PhpOffice\PhpPresentation\AbstractShape $shape - * @return \PhpOffice\PhpPresentation\AbstractShape - */ - public function addShape(AbstractShape $shape) - { - $shape->setContainer($this); - - return $shape; - } - - /** - * Create rich text shape - * - * @return \PhpOffice\PhpPresentation\Shape\RichText - */ - public function createRichTextShape() - { - $shape = new RichText(); - $this->addShape($shape); - - return $shape; - } - - /** - * Create line shape - * - * @param int $fromX Starting point x offset - * @param int $fromY Starting point y offset - * @param int $toX Ending point x offset - * @param int $toY Ending point y offset - * @return \PhpOffice\PhpPresentation\Shape\Line - */ - public function createLineShape($fromX, $fromY, $toX, $toY) - { - $shape = new Line($fromX, $fromY, $toX, $toY); - $this->addShape($shape); - - return $shape; - } - - /** - * Create chart shape - * - * @return \PhpOffice\PhpPresentation\Shape\Chart - */ - public function createChartShape() - { - $shape = new Chart(); - $this->addShape($shape); - - return $shape; - } - - /** - * Create drawing shape - * - * @return \PhpOffice\PhpPresentation\Shape\Drawing - */ - public function createDrawingShape() - { - $shape = new Drawing(); - $this->addShape($shape); - - return $shape; - } - - /** - * Create table shape - * - * @param int $columns Number of columns - * @return \PhpOffice\PhpPresentation\Shape\Table - */ - public function createTableShape($columns = 1) - { - $shape = new Table($columns); - $this->addShape($shape); - - return $shape; - } - - /** - * Creates a group within this slide - * - * @return \PhpOffice\PhpPresentation\Shape\Group - */ - public function createGroup() - { - $shape = new Group(); - $this->addShape($shape); - - return $shape; - } - - /** - * Get parent - * - * @return PhpPresentation - */ - public function getParent() - { - return $this->parent; - } - - /** - * Re-bind parent - * - * @param \PhpOffice\PhpPresentation\PhpPresentation $parent - * @return \PhpOffice\PhpPresentation\Slide - */ - public function rebindParent(PhpPresentation $parent) - { - $this->parent->removeSlideByIndex($this->parent->getIndex($this)); - $this->parent = $parent; - - return $this; - } - /** * Get slide layout * - * @return string + * @return SlideLayout */ public function getSlideLayout() { @@ -291,13 +96,12 @@ public function getSlideLayout() /** * Set slide layout * - * @param string $layout + * @param SlideLayout $layout * @return \PhpOffice\PhpPresentation\Slide */ - public function setSlideLayout($layout = Layout::BLANK) + public function setSlideLayout(SlideLayout $layout) { $this->slideLayout = $layout; - return $this; } @@ -324,42 +128,6 @@ public function setSlideMasterId($masterId = 1) return $this; } - /** - * Get hash code - * - * @return string Hash code - */ - public function getHashCode() - { - return md5($this->identifier . __CLASS__); - } - - /** - * Get hash index - * - * Note that this index may vary during script execution! Only reliable moment is - * while doing a write of a workbook and when changes are not allowed. - * - * @return string Hash index - */ - public function getHashIndex() - { - return $this->hashIndex; - } - - /** - * Set hash index - * - * Note that this index may vary during script execution! Only reliable moment is - * while doing a write of a workbook and when changes are not allowed. - * - * @param string $value Hash index - */ - public function setHashIndex($value) - { - $this->hashIndex = $value; - } - /** * Copy slide (!= clone!) * @@ -372,66 +140,6 @@ public function copy() return $copied; } - /** - * Get X Offset - * - * @return int - */ - public function getOffsetX() - { - if ($this->offsetX === null) { - $offsets = GeometryCalculator::calculateOffsets($this); - $this->offsetX = $offsets[GeometryCalculator::X]; - $this->offsetY = $offsets[GeometryCalculator::Y]; - } - return $this->offsetX; - } - - /** - * Get Y Offset - * - * @return int - */ - public function getOffsetY() - { - if ($this->offsetY === null) { - $offsets = GeometryCalculator::calculateOffsets($this); - $this->offsetX = $offsets[GeometryCalculator::X]; - $this->offsetY = $offsets[GeometryCalculator::Y]; - } - return $this->offsetY; - } - - /** - * Get X Extent - * - * @return int - */ - public function getExtentX() - { - if ($this->extentX === null) { - $extents = GeometryCalculator::calculateExtents($this); - $this->extentX = $extents[GeometryCalculator::X]; - $this->extentY = $extents[GeometryCalculator::Y]; - } - return $this->extentX; - } - - /** - * Get Y Extent - * - * @return int - */ - public function getExtentY() - { - if ($this->extentY === null) { - $extents = GeometryCalculator::calculateExtents($this); - $this->extentX = $extents[GeometryCalculator::X]; - $this->extentY = $extents[GeometryCalculator::Y]; - } - return $this->extentY; - } - /** * * @return \PhpOffice\PhpPresentation\Slide\Note @@ -458,61 +166,73 @@ public function setNote(Note $note = null) } /** - * - * @return \PhpOffice\PhpPresentation\Slide\Transition + * Get the name of the slide + * @return string */ - public function getTransition() + public function getName() { - return $this->slideTransition; + return $this->name; } /** - * - * @param \PhpOffice\PhpPresentation\Slide\Transition $transition - * @return \PhpOffice\PhpPresentation\Slide + * Set the name of the slide + * @param string $name + * @return $this */ - public function setTransition(Transition $transition = null) + public function setName($name = null) { - $this->slideTransition = $transition; - + $this->name = $name; return $this; } /** - * Get the name of the slide - * @return string + * @return boolean */ - public function getName() + public function isVisible() { - return $this->name; + return $this->isVisible; } /** - * Set the name of the slide - * @param string $name - * @return $this + * @param boolean $value + * @return Slide */ - public function setName($name = null) + public function setIsVisible($value = true) { - $this->name = $name; + $this->isVisible = (bool)$value; return $this; } /** - * @return AbstractBackground + * Add an animation to the slide + * + * @param \PhpOffice\PhpPresentation\Slide\Animation + * @return Slide */ - public function getBackground() + public function addAnimation($animation) { - return $this->background; + $this->animations[] = $animation; + return $this; } /** - * @param AbstractBackground $background - * @return $this + * Get collection of animations + * + * @return \PhpOffice\PhpPresentation\Slide\Animation[] + */ + public function getAnimations() + { + return $this->animations; + } + + /** + * Set collection of animations + * @param \PhpOffice\PhpPresentation\Slide\Animation[] $array + * @return Slide */ - public function setBackground(AbstractBackground $background = null) + public function setAnimations(array $array = array()) { - $this->background = $background; + $this->animations = $array; return $this; } } diff --git a/src/PhpPresentation/Slide/AbstractSlide.php b/src/PhpPresentation/Slide/AbstractSlide.php new file mode 100644 index 000000000..fa28559d4 --- /dev/null +++ b/src/PhpPresentation/Slide/AbstractSlide.php @@ -0,0 +1,381 @@ +shapeCollection; + } + + /** + * Get collection of shapes + * + * @return AbstractSlide + */ + public function setShapeCollection($shapeCollection = array()) + { + $this->shapeCollection = $shapeCollection; + return $this; + } + + /** + * Add shape to slide + * + * @param \PhpOffice\PhpPresentation\AbstractShape $shape + * @return \PhpOffice\PhpPresentation\AbstractShape + */ + public function addShape(AbstractShape $shape) + { + $shape->setContainer($this); + return $shape; + } + + /** + * Get X Offset + * + * @return int + */ + public function getOffsetX() + { + if ($this->offsetX === null) { + $offsets = GeometryCalculator::calculateOffsets($this); + $this->offsetX = $offsets[GeometryCalculator::X]; + $this->offsetY = $offsets[GeometryCalculator::Y]; + } + return $this->offsetX; + } + + /** + * Get Y Offset + * + * @return int + */ + public function getOffsetY() + { + if ($this->offsetY === null) { + $offsets = GeometryCalculator::calculateOffsets($this); + $this->offsetX = $offsets[GeometryCalculator::X]; + $this->offsetY = $offsets[GeometryCalculator::Y]; + } + return $this->offsetY; + } + + /** + * Get X Extent + * + * @return int + */ + public function getExtentX() + { + if ($this->extentX === null) { + $extents = GeometryCalculator::calculateExtents($this); + $this->extentX = $extents[GeometryCalculator::X]; + $this->extentY = $extents[GeometryCalculator::Y]; + } + return $this->extentX; + } + + /** + * Get Y Extent + * + * @return int + */ + public function getExtentY() + { + if ($this->extentY === null) { + $extents = GeometryCalculator::calculateExtents($this); + $this->extentX = $extents[GeometryCalculator::X]; + $this->extentY = $extents[GeometryCalculator::Y]; + } + return $this->extentY; + } + + /** + * Get hash code + * + * @return string Hash code + */ + public function getHashCode() + { + return md5($this->identifier . __CLASS__); + } + + /** + * Get hash index + * + * Note that this index may vary during script execution! Only reliable moment is + * while doing a write of a workbook and when changes are not allowed. + * + * @return string Hash index + */ + public function getHashIndex() + { + return $this->hashIndex; + } + + /** + * Set hash index + * + * Note that this index may vary during script execution! Only reliable moment is + * while doing a write of a workbook and when changes are not allowed. + * + * @param string $value Hash index + */ + public function setHashIndex($value) + { + $this->hashIndex = $value; + } + + /** + * Create rich text shape + * + * @return \PhpOffice\PhpPresentation\Shape\RichText + */ + public function createRichTextShape() + { + $shape = new RichText(); + $this->addShape($shape); + return $shape; + } + + /** + * Create line shape + * + * @param int $fromX Starting point x offset + * @param int $fromY Starting point y offset + * @param int $toX Ending point x offset + * @param int $toY Ending point y offset + * @return \PhpOffice\PhpPresentation\Shape\Line + */ + public function createLineShape($fromX, $fromY, $toX, $toY) + { + $shape = new Line($fromX, $fromY, $toX, $toY); + $this->addShape($shape); + return $shape; + } + + /** + * Create chart shape + * + * @return \PhpOffice\PhpPresentation\Shape\Chart + */ + public function createChartShape() + { + $shape = new Chart(); + $this->addShape($shape); + return $shape; + } + + /** + * Create drawing shape + * + * @return \PhpOffice\PhpPresentation\Shape\Drawing\File + */ + public function createDrawingShape() + { + $shape = new File(); + $this->addShape($shape); + return $shape; + } + + /** + * Create table shape + * + * @param int $columns Number of columns + * @return \PhpOffice\PhpPresentation\Shape\Table + */ + public function createTableShape($columns = 1) + { + $shape = new Table($columns); + $this->addShape($shape); + return $shape; + } + + /** + * Creates a group within this slide + * + * @return \PhpOffice\PhpPresentation\Shape\Group + */ + public function createGroup() + { + $shape = new Group(); + $this->addShape($shape); + return $shape; + } + + /** + * Get parent + * + * @return PhpPresentation + */ + public function getParent() + { + return $this->parent; + } + + /** + * Re-bind parent + * + * @param \PhpOffice\PhpPresentation\PhpPresentation $parent + * @return \PhpOffice\PhpPresentation\Slide + */ + public function rebindParent(PhpPresentation $parent) + { + $this->parent->removeSlideByIndex($this->parent->getIndex($this)); + $this->parent = $parent; + return $this; + } + + /** + * @return AbstractBackground + */ + public function getBackground() + { + return $this->background; + } + + /** + * @param AbstractBackground $background + * @return Slide + */ + public function setBackground(AbstractBackground $background = null) + { + $this->background = $background; + return $this; + } + + /** + * + * @return \PhpOffice\PhpPresentation\Slide\Transition + */ + public function getTransition() + { + return $this->slideTransition; + } + + /** + * + * @param \PhpOffice\PhpPresentation\Slide\Transition $transition + * @return \PhpOffice\PhpPresentation\Slide + */ + public function setTransition(Transition $transition = null) + { + $this->slideTransition = $transition; + return $this; + } + + /** + * @return string + */ + public function getRelsIndex() + { + return $this->relsIndex; + } + + /** + * @param string $indexName + */ + public function setRelsIndex($indexName) + { + $this->relsIndex = $indexName; + } +} diff --git a/src/PhpPresentation/Slide/Animation.php b/src/PhpPresentation/Slide/Animation.php new file mode 100644 index 000000000..54f77daa5 --- /dev/null +++ b/src/PhpPresentation/Slide/Animation.php @@ -0,0 +1,40 @@ +shapeCollection[] = $shape; + return $this; + } + + /** + * @return array + */ + public function getShapeCollection() + { + return $this->shapeCollection; + } + + /** + * @param array $array + * @return Animation + */ + public function setShapeCollection(array $array = array()) + { + $this->shapeCollection = $array; + return $this; + } +} diff --git a/src/PhpPresentation/Slide/Background/Image.php b/src/PhpPresentation/Slide/Background/Image.php index a838fd432..ba7789cb1 100644 --- a/src/PhpPresentation/Slide/Background/Image.php +++ b/src/PhpPresentation/Slide/Background/Image.php @@ -49,20 +49,16 @@ public function getPath() public function setPath($pValue = '', $pVerifyFile = true) { if ($pVerifyFile) { - if (file_exists($pValue)) { - $this->path = $pValue; - - if ($this->width == 0 && $this->height == 0) { - // Get width/height - list($this->width, $this->height) = getimagesize($pValue); - } - } else { + if (!file_exists($pValue)) { throw new \Exception("File $pValue not found!"); } - } else { - $this->path = $pValue; - } + if ($this->width == 0 && $this->height == 0) { + // Get width/height + list($this->width, $this->height) = getimagesize($pValue); + } + } + $this->path = $pValue; return $this; } diff --git a/src/PhpPresentation/Slide/Background/SchemeColor.php b/src/PhpPresentation/Slide/Background/SchemeColor.php new file mode 100644 index 000000000..51bba8793 --- /dev/null +++ b/src/PhpPresentation/Slide/Background/SchemeColor.php @@ -0,0 +1,32 @@ +schemeColor = $color; + return $this; + } + + /** + * @return StyleSchemeColor + */ + public function getSchemeColor() + { + return $this->schemeColor; + } +} diff --git a/src/PhpPresentation/Slide/SlideLayout.php b/src/PhpPresentation/Slide/SlideLayout.php new file mode 100644 index 000000000..0b11b9695 --- /dev/null +++ b/src/PhpPresentation/Slide/SlideLayout.php @@ -0,0 +1,91 @@ +slideMaster = $pSlideMaster; + // Shape collection + $this->shapeCollection = new \ArrayObject(); + // Set identifier + $this->identifier = md5(rand(0, 9999) . time()); + // Set a basic colorMap + $this->colorMap = new ColorMap(); + } + + /** + * @return int + */ + public function getLayoutName() + { + return $this->layoutName; + } + + /** + * @param int $layoutName + * @return SlideLayout + */ + public function setLayoutName($layoutName) + { + $this->layoutName = $layoutName; + return $this; + } +} diff --git a/src/PhpPresentation/Slide/SlideMaster.php b/src/PhpPresentation/Slide/SlideMaster.php new file mode 100644 index 000000000..ee5d6bd3b --- /dev/null +++ b/src/PhpPresentation/Slide/SlideMaster.php @@ -0,0 +1,168 @@ + '000000', + 'lt1' => 'FFFFFF', + 'dk2' => '1F497D', + 'lt2' => 'EEECE1', + 'accent1' => '4F81BD', + 'accent2' => 'C0504D', + 'accent3' => '9BBB59', + 'accent4' => '8064A2', + 'accent5' => '4BACC6', + 'accent6' => 'F79646', + 'hlink' => '0000FF', + 'folHlink' => '800080', + ); + + /** + * Create a new slideMaster + * + * @param PhpPresentation $pParent + */ + public function __construct(PhpPresentation $pParent = null) + { + // Set parent + $this->parent = $pParent; + // Shape collection + $this->shapeCollection = new \ArrayObject(); + // Set identifier + $this->identifier = md5(rand(0, 9999) . time()); + // Set a basic colorMap + $this->colorMap = new ColorMap(); + // Set a white background + $this->background = new BackgroundColor(); + $this->background->setColor(new Color(Color::COLOR_WHITE)); + // Set basic textStyles + $this->textStyles = new TextStyle(true); + // Set basic scheme colors + foreach ($this->defaultSchemeColor as $key => $value) { + $oSchemeColor = new SchemeColor(); + $oSchemeColor->setValue($key); + $oSchemeColor->setRGB($value); + $this->addSchemeColor($oSchemeColor); + } + } + + /** + * Create a slideLayout and add it to this presentation + * + * @return \PhpOffice\PhpPresentation\Slide\SlideLayout + */ + public function createSlideLayout() + { + $newSlideLayout = new SlideLayout($this); + $this->addSlideLayout($newSlideLayout); + return $newSlideLayout; + } + + /** + * Add slideLayout + * + * @param \PhpOffice\PhpPresentation\Slide\SlideLayout $slideLayout + * @throws \Exception + * @return \PhpOffice\PhpPresentation\Slide\SlideLayout + */ + public function addSlideLayout(SlideLayout $slideLayout = null) + { + $this->slideLayouts[] = $slideLayout; + return $slideLayout; + } + + /** + * @return SlideLayout[] + */ + public function getAllSlideLayouts() + { + return $this->slideLayouts; + } + + /** + * @return TextStyle + */ + public function getTextStyles() + { + return $this->textStyles; + } + + /** + * @param TextStyle $textStyle + * @return $this + */ + public function setTextStyles(TextStyle $textStyle) + { + $this->textStyles = $textStyle; + return $this; + } + + /** + * @param SchemeColor $schemeColor + * @return $this + */ + public function addSchemeColor(SchemeColor $schemeColor) + { + $this->arraySchemeColor[$schemeColor->getValue()] = $schemeColor; + return $this; + } + + /** + * @return \PhpOffice\PhpPresentation\Style\SchemeColor[] + */ + public function getAllSchemeColors() + { + return $this->arraySchemeColor; + } +} diff --git a/src/PhpPresentation/Style/Alignment.php b/src/PhpPresentation/Style/Alignment.php index 031093775..0ec142530 100644 --- a/src/PhpPresentation/Style/Alignment.php +++ b/src/PhpPresentation/Style/Alignment.php @@ -39,6 +39,12 @@ class Alignment implements ComparableInterface const VERTICAL_TOP = 't'; const VERTICAL_CENTER = 'ctr'; + private $supportedStyles = array( + self::HORIZONTAL_GENERAL, + self::HORIZONTAL_LEFT, + self::HORIZONTAL_RIGHT, + ); + /** * Horizontal * @@ -58,28 +64,28 @@ class Alignment implements ComparableInterface * * @var int */ - private $level; + private $level = 0; /** * Indent - only possible with horizontal alignment left and right * * @var int */ - private $indent; + private $indent = 0; /** * Margin left - only possible with horizontal alignment left and right * * @var int */ - private $marginLeft; + private $marginLeft = 0; /** * Margin right - only possible with horizontal alignment left and right * * @var int */ - private $marginRight; + private $marginRight = 0; /** * Hash index @@ -96,10 +102,6 @@ public function __construct() // Initialise values $this->horizontal = self::HORIZONTAL_LEFT; $this->vertical = self::VERTICAL_BASE; - $this->level = 0; - $this->indent = 0; - $this->marginLeft = 0; - $this->marginRight = 0; } /** @@ -199,10 +201,8 @@ public function getIndent() */ public function setIndent($pValue = 0) { - if ($pValue > 0) { - if ($this->getHorizontal() != self::HORIZONTAL_GENERAL && $this->getHorizontal() != self::HORIZONTAL_LEFT && $this->getHorizontal() != self::HORIZONTAL_RIGHT) { - $pValue = 0; // indent not supported - } + if ($pValue > 0 && !in_array($this->getHorizontal(), $this->supportedStyles)) { + $pValue = 0; // indent not supported } $this->indent = $pValue; @@ -228,10 +228,8 @@ public function getMarginLeft() */ public function setMarginLeft($pValue = 0) { - if ($pValue > 0) { - if ($this->getHorizontal() != self::HORIZONTAL_GENERAL && $this->getHorizontal() != self::HORIZONTAL_LEFT && $this->getHorizontal() != self::HORIZONTAL_RIGHT) { - $pValue = 0; // margin left not supported - } + if ($pValue > 0 && !in_array($this->getHorizontal(), $this->supportedStyles)) { + $pValue = 0; // margin left not supported } $this->marginLeft = $pValue; @@ -257,10 +255,8 @@ public function getMarginRight() */ public function setMarginRight($pValue = 0) { - if ($pValue > 0) { - if ($this->getHorizontal() != self::HORIZONTAL_GENERAL && $this->getHorizontal() != self::HORIZONTAL_LEFT && $this->getHorizontal() != self::HORIZONTAL_RIGHT) { - $pValue = 0; // margin left not supported - } + if ($pValue > 0 && !in_array($this->getHorizontal(), $this->supportedStyles)) { + $pValue = 0; // margin right not supported } $this->marginRight = $pValue; diff --git a/src/PhpPresentation/Style/Bullet.php b/src/PhpPresentation/Style/Bullet.php index 9ec2eb50b..de078a134 100644 --- a/src/PhpPresentation/Style/Bullet.php +++ b/src/PhpPresentation/Style/Bullet.php @@ -94,6 +94,13 @@ class Bullet implements ComparableInterface */ private $bulletChar = '-'; + /** + * Bullet char + * + * @var Color + */ + private $bulletColor; + /** * Bullet numeric style * @@ -124,6 +131,7 @@ public function __construct() $this->bulletType = self::TYPE_NONE; $this->bulletFont = 'Calibri'; $this->bulletChar = '-'; + $this->bulletColor = new Color(); $this->bulletNumericStyle = self::NUMERIC_DEFAULT; $this->bulletNumericStartAt = 1; } @@ -288,4 +296,22 @@ public function setHashIndex($value) { $this->hashIndex = $value; } + + /** + * @return Color + */ + public function getBulletColor() + { + return $this->bulletColor; + } + + /** + * @param Color $bulletColor + * @return Bullet + */ + public function setBulletColor(Color $bulletColor) + { + $this->bulletColor = $bulletColor; + return $this; + } } diff --git a/src/PhpPresentation/Style/Color.php b/src/PhpPresentation/Style/Color.php index d962fee08..48bb35f11 100644 --- a/src/PhpPresentation/Style/Color.php +++ b/src/PhpPresentation/Style/Color.php @@ -87,6 +87,21 @@ public function setARGB($pValue = self::COLOR_BLACK) return $this; } + /** + * Get the alpha % of the ARGB + * Will return 100 if no ARGB + * @return integer + */ + public function getAlpha() + { + $alpha = 100; + if (strlen($this->argb) >= 6) { + $dec = hexdec(substr($this->argb, 0, 2)); + $alpha = number_format(($dec/255) * 100, 2); + } + return $alpha; + } + /** * Get RGB * diff --git a/src/PhpPresentation/Style/ColorMap.php b/src/PhpPresentation/Style/ColorMap.php new file mode 100644 index 000000000..1f5dc7bbb --- /dev/null +++ b/src/PhpPresentation/Style/ColorMap.php @@ -0,0 +1,99 @@ + 'lt1', + self::COLOR_TX1 => 'dk1', + self::COLOR_BG2 => 'lt2', + self::COLOR_TX2 => 'dk2', + self::COLOR_ACCENT1 => 'accent1', + self::COLOR_ACCENT2 => 'accent2', + self::COLOR_ACCENT3 => 'accent3', + self::COLOR_ACCENT4 => 'accent4', + self::COLOR_ACCENT5 => 'accent5', + self::COLOR_ACCENT6 => 'accent6', + self::COLOR_HLINK => 'hlink', + self::COLOR_FOLHLINK => 'folHlink' + ); + + /** + * ColorMap constructor. + * Create a new ColorMap with standard values + */ + public function __construct() + { + $this->mapping = self::$mappingDefault; + } + + /** + * Change the color of one of the elements in the map + * + * @param string $item + * @param string $newThemeColor + */ + public function changeColor($item, $newThemeColor) + { + $this->mapping[$item] = $newThemeColor; + } + + /** + * Store a new map. For use with the reader + * + * @param array $arrayMapping + */ + public function setMapping(array $arrayMapping = array()) + { + $this->mapping = $arrayMapping; + } + + /** + * Get the whole mapping as an array + * + * @return array + */ + public function getMapping() + { + return $this->mapping; + } +} diff --git a/src/PhpPresentation/Style/Font.php b/src/PhpPresentation/Style/Font.php index 9fc1c68c3..226ae3df2 100644 --- a/src/PhpPresentation/Style/Font.php +++ b/src/PhpPresentation/Style/Font.php @@ -344,7 +344,7 @@ public function setStrikethrough($pValue = false) /** * Get Color * - * @return \PhpOffice\PhpPresentation\Style\Color + * @return \PhpOffice\PhpPresentation\Style\Color|\PhpOffice\PhpPresentation\Style\SchemeColor */ public function getColor() { @@ -354,12 +354,15 @@ public function getColor() /** * Set Color * - * @param \PhpOffice\PhpPresentation\Style\Color $pValue + * @param \PhpOffice\PhpPresentation\Style\Color|\PhpOffice\PhpPresentation\Style\SchemeColor $pValue * @throws \Exception * @return \PhpOffice\PhpPresentation\Style\Font */ - public function setColor(Color $pValue = null) + public function setColor($pValue = null) { + if (!$pValue instanceof Color) { + throw new \Exception('$pValue must be an instance of \PhpOffice\PhpPresentation\Style\Color'); + } $this->color = $pValue; return $this; diff --git a/src/PhpPresentation/Style/Outline.php b/src/PhpPresentation/Style/Outline.php new file mode 100644 index 000000000..9f03903b2 --- /dev/null +++ b/src/PhpPresentation/Style/Outline.php @@ -0,0 +1,78 @@ +fill = new Fill(); + } + + /** + * @return Fill + */ + public function getFill() + { + return $this->fill; + } + + /** + * @param Fill $fill + * @return Outline + */ + public function setFill(Fill $fill) + { + $this->fill = $fill; + return $this; + } + + /** + * @return int + */ + public function getWidth() + { + return $this->width; + } + + /** + * Value in points + * @param int $width + * @return Outline + */ + public function setWidth($width) + { + $this->width = intval($width); + return $this; + } +} diff --git a/src/PhpPresentation/Style/SchemeColor.php b/src/PhpPresentation/Style/SchemeColor.php new file mode 100644 index 000000000..a82a7386d --- /dev/null +++ b/src/PhpPresentation/Style/SchemeColor.php @@ -0,0 +1,44 @@ +value; + } + + /** + * @param string $value + */ + public function setValue($value) + { + $this->value = $value; + } +} diff --git a/src/PhpPresentation/Style/TextStyle.php b/src/PhpPresentation/Style/TextStyle.php new file mode 100644 index 000000000..7358eddc4 --- /dev/null +++ b/src/PhpPresentation/Style/TextStyle.php @@ -0,0 +1,168 @@ +getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); + $oRTParagraph->getFont()->setSize(44)->setColor(new SchemeColor())->getColor()->setValue("lt1"); + $this->titleStyle[1] = $oRTParagraph; + $oRTParagraph = new RichTextParagraph(); + $oRTParagraph->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER) + ->setIndent(-324900 / 9525) + ->setMarginLeft(342900 / 9525); + $oRTParagraph->getFont()->setSize(32)->setColor(new SchemeColor())->getColor()->setValue("tx1"); + $this->bodyStyle[1] = $oRTParagraph; + $oRTParagraph = new RichTextParagraph(); + $oRTParagraph->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); + $oRTParagraph->getFont()->setSize(10)->setColor(new SchemeColor())->getColor()->setValue("tx1"); + $this->otherStyle[0] = $oRTParagraph; + } + } + + /** + * @param $lvl + * @return bool + */ + private function checkLvl($lvl) + { + if (!is_int($lvl)) { + return false; + } + if ($lvl > 9) { + return false; + } + return true; + } + + /** + * @param RichTextParagraph $style + * @param $lvl + */ + public function setBodyStyleAtLvl(RichTextParagraph $style, $lvl) + { + if (!$this->checkLvl($lvl)) { + $this->bodyStyle[$lvl] = $style; + } + } + + /** + * @param RichTextParagraph $style + * @param $lvl + */ + public function setTitleStyleAtLvl(RichTextParagraph $style, $lvl) + { + if (!$this->checkLvl($lvl)) { + $this->titleStyle[$lvl] = $style; + } + } + + /** + * @param RichTextParagraph $style + * @param $lvl + */ + public function setOtherStyleAtLvl(RichTextParagraph $style, $lvl) + { + if (!$this->checkLvl($lvl)) { + $this->otherStyle[$lvl] = $style; + } + } + + /** + * @param $lvl + * @return mixed + */ + public function getBodyStyleAtLvl($lvl) + { + if (!$this->checkLvl($lvl)) { + return $this->bodyStyle[$lvl]; + } + } + + /** + * @param $lvl + * @return mixed + */ + public function getTitleStyleAtLvl($lvl) + { + if (!$this->checkLvl($lvl)) { + return $this->bodyStyle[$lvl]; + } + } + + /** + * @param $lvl + * @return mixed + */ + public function getOtherStyleAtLvl($lvl) + { + if (!$this->checkLvl($lvl)) { + return $this->bodyStyle[$lvl]; + } + } + + /** + * @return array + */ + public function getBodyStyle() + { + return $this->bodyStyle; + } + + /** + * @return array + */ + public function getTitleStyle() + { + return $this->titleStyle; + } + + /** + * @return array + */ + public function getOtherStyle() + { + return $this->otherStyle; + } +} diff --git a/src/PhpPresentation/Writer/AbstractDecoratorWriter.php b/src/PhpPresentation/Writer/AbstractDecoratorWriter.php new file mode 100644 index 000000000..1d174040d --- /dev/null +++ b/src/PhpPresentation/Writer/AbstractDecoratorWriter.php @@ -0,0 +1,84 @@ +oHashTable = $hashTable; + return $this; + } + + /** + * @return HashTable + */ + public function getDrawingHashTable() + { + return $this->oHashTable; + } + + /** + * @param PhpPresentation $oPresentation + * @return $this + */ + public function setPresentation(PhpPresentation $oPresentation) + { + $this->oPresentation = $oPresentation; + return $this; + } + + /** + * @return PhpPresentation + */ + public function getPresentation() + { + return $this->oPresentation; + } + + /** + * @param ZipInterface $oZip + * @return $this + */ + public function setZip(ZipInterface $oZip) + { + $this->oZip = $oZip; + return $this; + } + + /** + * @return ZipInterface + */ + public function getZip() + { + return $this->oZip; + } +} diff --git a/src/PhpPresentation/Writer/AbstractWriter.php b/src/PhpPresentation/Writer/AbstractWriter.php new file mode 100644 index 000000000..2304c0675 --- /dev/null +++ b/src/PhpPresentation/Writer/AbstractWriter.php @@ -0,0 +1,128 @@ +oDrawingHashTable; + } + + /** + * Get PhpPresentation object + * + * @return PhpPresentation + * @throws \Exception + */ + public function getPhpPresentation() + { + if (empty($this->oPresentation)) { + throw new \Exception("No PhpPresentation assigned."); + } + return $this->oPresentation; + } + + /** + * Get PhpPresentation object + * + * @param PhpPresentation $pPhpPresentation PhpPresentation object + * @throws \Exception + * @return \PhpOffice\PhpPresentation\Writer\ODPresentation + */ + public function setPhpPresentation(PhpPresentation $pPhpPresentation = null) + { + $this->oPresentation = $pPhpPresentation; + return $this; + } + + + /** + * @param ZipInterface $oZipAdapter + * @return $this + */ + public function setZipAdapter(ZipInterface $oZipAdapter) + { + $this->oZipAdapter = $oZipAdapter; + return $this; + } + + /** + * @return ZipInterface + */ + public function getZipAdapter() + { + return $this->oZipAdapter; + } + + /** + * Get an array of all drawings + * + * @return \PhpOffice\PhpPresentation\Shape\AbstractDrawing[] All drawings in PhpPresentation + * @throws \Exception + */ + protected function allDrawings() + { + // Get an array of all drawings + $aDrawings = array(); + + // Loop trough PhpPresentation + foreach ($this->getPhpPresentation()->getAllSlides() as $oSlide) { + $arrayReturn = $this->iterateCollection($oSlide->getShapeCollection()->getIterator()); + $aDrawings = array_merge($aDrawings, $arrayReturn); + } + return $aDrawings; + } + + private function iterateCollection(\ArrayIterator $oIterator) + { + $arrayReturn = array(); + if ($oIterator->count() <= 0) { + return $arrayReturn; + } + + while ($oIterator->valid()) { + $oShape = $oIterator->current(); + if ($oShape instanceof AbstractDrawingAdapter) { + $arrayReturn[] = $oShape; + } elseif ($oShape instanceof Chart) { + $arrayReturn[] = $oShape; + } elseif ($oShape instanceof Group) { + $arrayGroup = $this->iterateCollection($oShape->getShapeCollection()->getIterator()); + $arrayReturn = array_merge($arrayReturn, $arrayGroup); + } + $oIterator->next(); + } + return $arrayReturn; + } +} diff --git a/src/PhpPresentation/Writer/ODPresentation.php b/src/PhpPresentation/Writer/ODPresentation.php index b14b1f3db..8408c29de 100644 --- a/src/PhpPresentation/Writer/ODPresentation.php +++ b/src/PhpPresentation/Writer/ODPresentation.php @@ -17,46 +17,18 @@ namespace PhpOffice\PhpPresentation\Writer; +use PhpOffice\Common\Adapter\Zip\ZipArchiveAdapter; use PhpOffice\PhpPresentation\HashTable; use PhpOffice\PhpPresentation\PhpPresentation; -use PhpOffice\PhpPresentation\Shape\Drawing as ShapeDrawing; -use PhpOffice\PhpPresentation\Shape\MemoryDrawing; -use PhpOffice\PhpPresentation\Slide\Background\Image; -use PhpOffice\PhpPresentation\Writer\ODPresentation\Content; -use PhpOffice\PhpPresentation\Writer\ODPresentation\Drawing; -use PhpOffice\PhpPresentation\Writer\ODPresentation\Manifest; -use PhpOffice\PhpPresentation\Writer\ODPresentation\Meta; -use PhpOffice\PhpPresentation\Writer\ODPresentation\Mimetype; -use PhpOffice\PhpPresentation\Writer\ODPresentation\ObjectsChart; -use PhpOffice\PhpPresentation\Writer\ODPresentation\Styles; use PhpOffice\PhpPresentation\Shape\AbstractDrawing; +use PhpOffice\PhpPresentation\Shape\Table; +use DirectoryIterator; /** * ODPresentation writer */ -class ODPresentation implements WriterInterface +class ODPresentation extends AbstractWriter implements WriterInterface { - /** - * Private PhpPresentation - * - * @var \PhpOffice\PhpPresentation\PhpPresentation - */ - private $presentation; - - /** - * Private writer parts - * - * @var \PhpOffice\PhpPresentation\Writer\ODPresentation\AbstractPart[] - */ - private $writerParts; - - /** - * Private unique hashtable - * - * @var \PhpOffice\PhpPresentation\HashTable - */ - private $drawingHashTable; - /** * @var \PhpOffice\PhpPresentation\Shape\Chart[] */ @@ -89,22 +61,10 @@ public function __construct(PhpPresentation $pPhpPresentation = null) // Set up disk caching location $this->diskCachingDirectory = './'; - // Initialise writer parts - $this->writerParts['content'] = new Content(); - $this->writerParts['manifest'] = new Manifest(); - $this->writerParts['meta'] = new Meta(); - $this->writerParts['mimetype'] = new Mimetype(); - $this->writerParts['styles'] = new Styles(); - $this->writerParts['charts'] = new ObjectsChart(); - $this->writerParts['drawing'] = new Drawing(); - - // Assign parent WriterInterface - foreach ($this->writerParts as $writer) { - $writer->setParentWriter($this); - } - // Set HashTable variables - $this->drawingHashTable = new HashTable(); + $this->oDrawingHashTable = new HashTable(); + + $this->setZipAdapter(new ZipArchiveAdapter()); } /** @@ -118,203 +78,66 @@ public function save($pFilename) if (empty($pFilename)) { throw new \Exception("Filename is empty"); } - if (!is_null($this->presentation)) { - // If $pFilename is php://output or php://stdout, make it a temporary file... - $originalFilename = $pFilename; - if (strtolower($pFilename) == 'php://output' || strtolower($pFilename) == 'php://stdout') { - $pFilename = @tempnam('./', 'phppttmp'); - if ($pFilename == '') { - $pFilename = $originalFilename; - } - } - - $writerPartChart = $this->getWriterPart('charts'); - if (!$writerPartChart instanceof ObjectsChart) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\ODPresentation\ObjectsChart'); - } - $writerPartContent = $this->getWriterPart('content'); - if (!$writerPartContent instanceof Content) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\ODPresentation\Content'); - } - $writerPartDrawing = $this->getWriterPart('Drawing'); - if (!$writerPartDrawing instanceof Drawing) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\ODPresentation\Drawing'); - } - $writerPartManifest = $this->getWriterPart('manifest'); - if (!$writerPartManifest instanceof Manifest) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\ODPresentation\Manifest'); - } - $writerPartMeta = $this->getWriterPart('meta'); - if (!$writerPartMeta instanceof Meta) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\ODPresentation\Meta'); - } - $writerPartMimetype = $this->getWriterPart('mimetype'); - if (!$writerPartMimetype instanceof Mimetype) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\ODPresentation\Mimetype'); - } - $writerPartStyles = $this->getWriterPart('styles'); - if (!$writerPartStyles instanceof Styles) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\ODPresentation\Styles'); - } - - // Create drawing dictionary - $this->drawingHashTable->addFromSource($writerPartDrawing->allDrawings($this->presentation)); - - // Create new ZIP file and open it for writing - $objZip = new \ZipArchive(); - - // Try opening the ZIP file - if ($objZip->open($pFilename, \ZIPARCHIVE::OVERWRITE) !== true) { - if ($objZip->open($pFilename, \ZIPARCHIVE::CREATE) !== true) { - throw new \Exception("Could not open " . $pFilename . " for writing."); - } - } - - // Add mimetype to ZIP file - //@todo Not in ZIPARCHIVE::CM_STORE mode - $objZip->addFromString('mimetype', $writerPartMimetype->writePart()); - - // Add content.xml to ZIP file - $objZip->addFromString('content.xml', $writerPartContent->writePart($this->presentation)); - - // Add meta.xml to ZIP file - $objZip->addFromString('meta.xml', $writerPartMeta->writePart($this->presentation)); - - // Add styles.xml to ZIP file - $objZip->addFromString('styles.xml', $writerPartStyles->writePart($this->presentation)); - - // Add META-INF/manifest.xml - $objZip->addFromString('META-INF/manifest.xml', $writerPartManifest->writePart()); - - // Add charts - foreach ($this->chartArray as $keyChart => $shapeChart) { - $arrayFile = $writerPartChart->writePart($shapeChart); - foreach ($arrayFile as $file => $content) { - if (!empty($content)) { - $objZip->addFromString('Object '.$keyChart.'/' . $file, $content); - } - } + // If $pFilename is php://output or php://stdout, make it a temporary file... + $originalFilename = $pFilename; + if (strtolower($pFilename) == 'php://output' || strtolower($pFilename) == 'php://stdout') { + $pFilename = @tempnam('./', 'phppttmp'); + if ($pFilename == '') { + $pFilename = $originalFilename; } - - // Add media - $arrMedia = array(); - for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) { - $shape = $this->getDrawingHashTable()->getByIndex($i); - if (!($shape instanceof AbstractDrawing)) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Shape\AbstractDrawing'); - } - if ($shape instanceof ShapeDrawing) { - if (!in_array(md5($shape->getPath()), $arrMedia)) { - $arrMedia[] = md5($shape->getPath()); - - $imagePath = $shape->getPath(); + } - if (strpos($imagePath, 'zip://') !== false) { - $imagePath = substr($imagePath, 6); - $imagePathSplitted = explode('#', $imagePath); + // Initialize HashTable + $this->getDrawingHashTable()->addFromSource($this->allDrawings()); - $imageZip = new \ZipArchive(); - $imageZip->open($imagePathSplitted[0]); - $imageContents = $imageZip->getFromName($imagePathSplitted[1]); - $imageZip->close(); - unset($imageZip); - } else { - $imageContents = file_get_contents($imagePath); - } + // Initialize Zip + $oZip = $this->getZipAdapter(); + $oZip->open($pFilename); - $objZip->addFromString('Pictures/' . md5($shape->getPath()).'.'.$shape->getExtension(), $imageContents); - } - } elseif ($shape instanceof MemoryDrawing) { - if (!in_array(str_replace(' ', '_', $shape->getIndexedFilename()), $arrMedia)) { - $arrMedia[] = str_replace(' ', '_', $shape->getIndexedFilename()); - ob_start(); - call_user_func($shape->getRenderingFunction(), $shape->getImageResource()); - $imageContents = ob_get_contents(); - ob_end_clean(); + // Variables + $oPresentation = $this->getPhpPresentation(); + $arrayChart = array(); - $objZip->addFromString('Pictures/' . str_replace(' ', '_', $shape->getIndexedFilename()), $imageContents); - } - } + $arrayFiles = array(); + $oDir = new DirectoryIterator(dirname(__FILE__).DIRECTORY_SEPARATOR.'ODPresentation'); + foreach ($oDir as $oFile) { + if (!$oFile->isFile()) { + continue; } - foreach ($this->presentation->getAllSlides() as $keySlide => $oSlide) { - // Add background image slide - $oBkgImage = $oSlide->getBackground(); - if ($oBkgImage instanceof Image) { - $objZip->addFromString('Pictures/'.$oBkgImage->getIndexedFilename($keySlide), file_get_contents($oBkgImage->getPath())); - } - } + $class = __NAMESPACE__ . '\\ODPresentation\\' . $oFile->getBasename('.php'); + $o = new \ReflectionClass($class); - // Close file - if ($objZip->close() === false) { - throw new \Exception("Could not close zip file $pFilename."); + if ($o->isAbstract() || !$o->isSubclassOf('PhpOffice\PhpPresentation\Writer\ODPresentation\AbstractDecoratorWriter')) { + continue; } - - // If a temporary file was used, copy it to the correct file stream - if ($originalFilename != $pFilename) { - if (copy($pFilename, $originalFilename) === false) { - throw new \Exception("Could not copy temporary zip file $pFilename to $originalFilename."); - } - if (@unlink($pFilename) === false) { - throw new \Exception('The file '.$pFilename.' could not be deleted.'); - } - } - } else { - throw new \Exception("PhpPresentation object unassigned."); + $arrayFiles[$oFile->getBasename('.php')] = $o; } - } - /** - * Get PhpPresentation object - * - * @return PhpPresentation - * @throws \Exception - */ - public function getPhpPresentation() - { - if (!is_null($this->presentation)) { - return $this->presentation; - } else { - throw new \Exception("No PhpPresentation assigned."); + ksort($arrayFiles); + + foreach ($arrayFiles as $o) { + $oService = $o->newInstance(); + $oService->setZip($oZip); + $oService->setPresentation($oPresentation); + $oService->setDrawingHashTable($this->getDrawingHashTable()); + $oService->setArrayChart($arrayChart); + $oZip = $oService->render(); + $arrayChart = $oService->getArrayChart(); + unset($oService); } - } - - /** - * Get PhpPresentation object - * - * @param PhpPresentation $pPhpPresentation PhpPresentation object - * @throws \Exception - * @return \PhpOffice\PhpPresentation\Writer\ODPresentation - */ - public function setPhpPresentation(PhpPresentation $pPhpPresentation = null) - { - $this->presentation = $pPhpPresentation; - return $this; - } + // Close file + $oZip->close(); - /** - * Get drawing hash table - * - * @return \PhpOffice\PhpPresentation\HashTable - */ - public function getDrawingHashTable() - { - return $this->drawingHashTable; - } - - /** - * Get writer part - * - * @param string $pPartName Writer part name - * @return \PhpOffice\PhpPresentation\Writer\ODPresentation\AbstractPart - */ - public function getWriterPart($pPartName = '') - { - if ($pPartName != '' && isset($this->writerParts[strtolower($pPartName)])) { - return $this->writerParts[strtolower($pPartName)]; - } else { - return null; + // If a temporary file was used, copy it to the correct file stream + if ($originalFilename != $pFilename) { + if (copy($pFilename, $originalFilename) === false) { + throw new \Exception("Could not copy temporary zip file $pFilename to $originalFilename."); + } + if (@unlink($pFilename) === false) { + throw new \Exception('The file ' . $pFilename . ' could not be removed.'); + } } } @@ -341,11 +164,10 @@ public function setUseDiskCaching($pValue = false, $pDirectory = null) $this->useDiskCaching = $pValue; if (!is_null($pDirectory)) { - if (is_dir($pDirectory)) { - $this->diskCachingDirectory = $pDirectory; - } else { + if (!is_dir($pDirectory)) { throw new \Exception("Directory does not exist: $pDirectory"); } + $this->diskCachingDirectory = $pDirectory; } return $this; diff --git a/src/PhpPresentation/Writer/ODPresentation/AbstractDecoratorWriter.php b/src/PhpPresentation/Writer/ODPresentation/AbstractDecoratorWriter.php new file mode 100644 index 000000000..c5e390e70 --- /dev/null +++ b/src/PhpPresentation/Writer/ODPresentation/AbstractDecoratorWriter.php @@ -0,0 +1,31 @@ +arrayChart; + } + + /** + * @param \PhpOffice\PhpPresentation\Shape\Chart[] $arrayChart + * @return AbstractDecoratorWriter + */ + public function setArrayChart($arrayChart) + { + $this->arrayChart = $arrayChart; + return $this; + } +} diff --git a/src/PhpPresentation/Writer/ODPresentation/AbstractPart.php b/src/PhpPresentation/Writer/ODPresentation/AbstractPart.php deleted file mode 100644 index 636b6b001..000000000 --- a/src/PhpPresentation/Writer/ODPresentation/AbstractPart.php +++ /dev/null @@ -1,80 +0,0 @@ -parentWriter = $pWriter; - } - - /** - * Get parent WriterInterface object - * - * @return \PhpOffice\PhpPresentation\Writer\WriterInterface - * @throws \Exception - */ - public function getParentWriter() - { - if (!is_null($this->parentWriter)) { - return $this->parentWriter; - } else { - throw new \Exception("No parent \PhpOffice\PhpPresentation\Writer\WriterInterface assigned."); - } - } - - /** - * Get XML Writer - */ - protected function getXMLWriter() - { - $parentWriter = $this->getParentWriter(); - if (!$parentWriter instanceof ODPresentation) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\ODPresentation'); - } - if ($parentWriter->hasDiskCaching()) { - return new XMLWriter(XMLWriter::STORAGE_DISK, $parentWriter->getDiskCachingDirectory()); - } else { - return new XMLWriter(XMLWriter::STORAGE_MEMORY); - } - } -} diff --git a/src/PhpPresentation/Writer/ODPresentation/Content.php b/src/PhpPresentation/Writer/ODPresentation/Content.php index fce2eddae..5e0a14158 100644 --- a/src/PhpPresentation/Writer/ODPresentation/Content.php +++ b/src/PhpPresentation/Writer/ODPresentation/Content.php @@ -1,33 +1,19 @@ getZip()->addFromString('content.xml', $this->writeContent()); + return $this->getZip(); + } + + /** * Write content file to XML format * - * @param PhpPresentation $pPhpPresentation * @return string XML Output * @throws \Exception */ - public function writePart(PhpPresentation $pPhpPresentation) + public function writeContent() { // Create XML writer - $objWriter = $this->getXMLWriter(); - - // XML header + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); $objWriter->startDocument('1.0', 'UTF-8'); // office:document-content @@ -122,6 +112,7 @@ public function writePart(PhpPresentation $pPhpPresentation) $objWriter->writeAttribute('xmlns:of', 'urn:oasis:names:tc:opendocument:xmlns:of:1.2'); $objWriter->writeAttribute('xmlns:rdfa', 'http://docs.oasis-open.org/opendocument/meta/rdfa#'); $objWriter->writeAttribute('xmlns:field', 'urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0'); + $objWriter->writeAttribute('xmlns:officeooo', 'http://openoffice.org/2009/office'); $objWriter->writeAttribute('office:version', '1.2'); // office:automatic-styles @@ -129,7 +120,7 @@ public function writePart(PhpPresentation $pPhpPresentation) $this->shapeId = 0; $incSlide = 0; - foreach ($pPhpPresentation->getAllSlides() as $pSlide) { + foreach ($this->getPresentation()->getAllSlides() as $pSlide) { // Slides $this->writeStyleSlide($objWriter, $pSlide, $incSlide); @@ -240,13 +231,15 @@ public function writePart(PhpPresentation $pPhpPresentation) $objWriter->writeAttribute('style:family', 'text'); // style:text-properties $objWriter->startElement('style:text-properties'); - $objWriter->writeAttribute('fo:color', '#' . $item->getColor()->getRGB()); - $objWriter->writeAttribute('fo:font-family', $item->getName()); - $objWriter->writeAttribute('fo:font-size', $item->getSize() . 'pt'); + $objWriter->writeAttribute('fo:color', '#' . $item->getFont()->getColor()->getRGB()); + $objWriter->writeAttribute('fo:font-family', $item->getFont()->getName()); + $objWriter->writeAttribute('fo:font-size', $item->getFont()->getSize() . 'pt'); // @todo : fo:font-style - if ($item->isBold()) { + if ($item->getFont()->isBold()) { $objWriter->writeAttribute('fo:font-weight', 'bold'); } + $objWriter->writeAttribute('fo:language', ($item->getLanguage() ? $item->getLanguage() : 'en-US')); + // @todo : style:text-underline-style $objWriter->endElement(); $objWriter->endElement(); @@ -263,10 +256,10 @@ public function writePart(PhpPresentation $pPhpPresentation) $objWriter->startElement('office:presentation'); // Write slides - $slideCount = $pPhpPresentation->getSlideCount(); + $slideCount = $this->getPresentation()->getSlideCount(); $this->shapeId = 0; for ($i = 0; $i < $slideCount; ++$i) { - $pSlide = $pPhpPresentation->getSlide($i); + $pSlide = $this->getPresentation()->getSlide($i); $objWriter->startElement('draw:page'); $name = $pSlide->getName(); if (!is_null($name)) { @@ -289,21 +282,25 @@ public function writePart(PhpPresentation $pPhpPresentation) $this->writeShapeLine($objWriter, $shape); } elseif ($shape instanceof Chart) { $this->writeShapeChart($objWriter, $shape); - } elseif ($shape instanceof AbstractDrawing) { - $this->writeShapePic($objWriter, $shape); + } elseif ($shape instanceof Media) { + $this->writeShapeMedia($objWriter, $shape); + } elseif ($shape instanceof ShapeDrawing\AbstractDrawingAdapter) { + $this->writeShapeDrawing($objWriter, $shape); } elseif ($shape instanceof Group) { $this->writeShapeGroup($objWriter, $shape); + } elseif ($shape instanceof Comment) { + $this->writeShapeComment($objWriter, $shape); } } // Slide Note if ($pSlide->getNote() instanceof Note) { $this->writeSlideNote($objWriter, $pSlide->getNote()); } - + $objWriter->endElement(); } - - if ($pPhpPresentation->getPresentationProperties()->isLoopContinuouslyUntilEsc()) { + + if ($this->getPresentation()->getPresentationProperties()->isLoopContinuouslyUntilEsc()) { $objWriter->startElement('presentation:settings'); $objWriter->writeAttribute('presentation:endless', 'true'); $objWriter->writeAttribute('presentation:pause', 'P0s'); @@ -321,13 +318,61 @@ public function writePart(PhpPresentation $pPhpPresentation) return $objWriter->getData(); } + /** + * Write picture + * + * @param \PhpOffice\Common\XMLWriter $objWriter + * @param \PhpOffice\PhpPresentation\Shape\Media $shape + */ + public function writeShapeMedia(XMLWriter $objWriter, Media $shape) + { + // draw:frame + $objWriter->startElement('draw:frame'); + $objWriter->writeAttribute('draw:name', $shape->getName()); + $objWriter->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getWidth()), 3) . 'cm'); + $objWriter->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getHeight()), 3) . 'cm'); + $objWriter->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetX()), 3) . 'cm'); + $objWriter->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetY()), 3) . 'cm'); + $objWriter->writeAttribute('draw:style-name', 'gr' . $this->shapeId); + // draw:frame > draw:plugin + $objWriter->startElement('draw:plugin'); + $objWriter->writeAttribute('xlink:href', 'Pictures/' . $shape->getIndexedFilename()); + $objWriter->writeAttribute('xlink:type', 'simple'); + $objWriter->writeAttribute('xlink:show', 'embed'); + $objWriter->writeAttribute('xlink:actuate', 'onLoad'); + $objWriter->writeAttribute('draw:mime-type', 'application/vnd.sun.star.media'); + + $objWriter->startElement('draw:param'); + $objWriter->writeAttribute('draw:name', 'Loop'); + $objWriter->writeAttribute('draw:value', 'false'); + $objWriter->endElement(); + $objWriter->startElement('draw:param'); + $objWriter->writeAttribute('draw:name', 'Mute'); + $objWriter->writeAttribute('draw:value', 'false'); + $objWriter->endElement(); + $objWriter->startElement('draw:param'); + $objWriter->writeAttribute('draw:name', 'VolumeDB'); + $objWriter->writeAttribute('draw:value', 0); + $objWriter->endElement(); + $objWriter->startElement('draw:param'); + $objWriter->writeAttribute('draw:name', 'Zoom'); + $objWriter->writeAttribute('draw:value', 'fit'); + $objWriter->endElement(); + + // draw:frame > ## draw:plugin + $objWriter->endElement(); + + // ## draw:frame + $objWriter->endElement(); + } + /** * Write picture * * @param \PhpOffice\Common\XMLWriter $objWriter * @param \PhpOffice\PhpPresentation\Shape\AbstractDrawing $shape */ - public function writeShapePic(XMLWriter $objWriter, AbstractDrawing $shape) + public function writeShapeDrawing(XMLWriter $objWriter, ShapeDrawing\AbstractDrawingAdapter $shape) { // draw:frame $objWriter->startElement('draw:frame'); @@ -339,9 +384,7 @@ public function writeShapePic(XMLWriter $objWriter, AbstractDrawing $shape) $objWriter->writeAttribute('draw:style-name', 'gr' . $this->shapeId); // draw:image $objWriter->startElement('draw:image'); - if ($shape instanceof ShapeDrawing) { - $objWriter->writeAttribute('xlink:href', 'Pictures/' . md5($shape->getPath()) . '.' . $shape->getExtension()); - } elseif ($shape instanceof MemoryDrawing) { + if ($shape instanceof ShapeDrawing\AbstractDrawingAdapter) { $objWriter->writeAttribute('xlink:href', 'Pictures/' . $shape->getIndexedFilename()); } $objWriter->writeAttribute('xlink:type', 'simple'); @@ -349,7 +392,7 @@ public function writeShapePic(XMLWriter $objWriter, AbstractDrawing $shape) $objWriter->writeAttribute('xlink:actuate', 'onLoad'); $objWriter->writeElement('text:p'); $objWriter->endElement(); - + if ($shape->hasHyperlink()) { // office:event-listeners $objWriter->startElement('office:event-listeners'); @@ -366,7 +409,7 @@ public function writeShapePic(XMLWriter $objWriter, AbstractDrawing $shape) // > office:event-listeners $objWriter->endElement(); } - + $objWriter->endElement(); } @@ -387,7 +430,7 @@ public function writeShapeTxt(XMLWriter $objWriter, RichText $shape) $objWriter->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetY()), 3) . 'cm'); // draw:text-box $objWriter->startElement('draw:text-box'); - + $paragraphs = $shape->getParagraphs(); $paragraphId = 0; $sCstShpLastBullet = ''; @@ -422,7 +465,7 @@ public function writeShapeTxt(XMLWriter $objWriter, RichText $shape) // text:span $objWriter->startElement('text:span'); if ($richtext instanceof Run) { - $objWriter->writeAttribute('text:style-name', 'T_' . $richtext->getFont()->getHashCode()); + $objWriter->writeAttribute('text:style-name', 'T_' . $richtext->getHashCode()); } if ($richtext->hasHyperlink() === true && $richtext->getHyperlink()->getUrl() != '') { // text:a @@ -446,9 +489,9 @@ public function writeShapeTxt(XMLWriter $objWriter, RichText $shape) } } $objWriter->endElement(); - //=============================================== - // Bullet list - //=============================================== + //=============================================== + // Bullet list + //=============================================== } elseif ($paragraph->getBulletStyle()->getBulletType() == 'bullet') { $bCstShpHasBullet = true; // Open the bullet list @@ -487,7 +530,7 @@ public function writeShapeTxt(XMLWriter $objWriter, RichText $shape) // text:span $objWriter->startElement('text:span'); if ($richtext instanceof Run) { - $objWriter->writeAttribute('text:style-name', 'T_' . $richtext->getFont()->getHashCode()); + $objWriter->writeAttribute('text:style-name', 'T_' . $richtext->getHashCode()); } if ($richtext->hasHyperlink() === true && $richtext->getHyperlink()->getUrl() != '') { // text:a @@ -525,12 +568,33 @@ public function writeShapeTxt(XMLWriter $objWriter, RichText $shape) $objWriter->endElement(); } } - + // > draw:text-box $objWriter->endElement(); // > draw:frame $objWriter->endElement(); } + /** + * Write Comment + * @param XMLWriter $objWriter + * @param Comment $oShape + */ + public function writeShapeComment(XMLWriter $objWriter, Comment $oShape) + { + // officeooo:annotation + $objWriter->startElement('officeooo:annotation'); + $objWriter->writeAttribute('svg:x', number_format(CommonDrawing::pixelsToCentimeters($oShape->getOffsetX()), 2, '.', '').'cm'); + $objWriter->writeAttribute('svg:y', number_format(CommonDrawing::pixelsToCentimeters($oShape->getOffsetY()), 2, '.', '').'cm'); + + if ($oShape->getAuthor() instanceof Comment\Author) { + $objWriter->writeElement('dc:creator', $oShape->getAuthor()->getName()); + } + $objWriter->writeElement('dc:date', date('Y-m-d\TH:i:s', $oShape->getDate())); + $objWriter->writeElement('text:p', $oShape->getText()); + + // ## officeooo:annotation + $objWriter->endElement(); + } /** * @param XMLWriter $objWriter @@ -546,8 +610,8 @@ public function writeShapeLine(XMLWriter $objWriter, Line $shape) $objWriter->writeAttribute('svg:x2', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetX()+$shape->getWidth()), 3) . 'cm'); $objWriter->writeAttribute('svg:y2', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetY()+$shape->getHeight()), 3) . 'cm'); - // text:p - $objWriter->writeElement('text:p'); + // text:p + $objWriter->writeElement('text:p'); $objWriter->endElement(); } @@ -565,16 +629,16 @@ public function writeShapeTable(XMLWriter $objWriter, Table $shape) $objWriter->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetY()), 3) . 'cm'); $objWriter->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getHeight()), 3) . 'cm'); $objWriter->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getWidth()), 3) . 'cm'); - + // table:table $objWriter->startElement('table:table'); - + foreach ($shape->getRows() as $keyRow => $shapeRow) { // table:table-row $objWriter->startElement('table:table-row'); $objWriter->writeAttribute('table:style-name', 'gr'.$this->shapeId.'r'.$keyRow); //@todo getFill - + $numColspan = 0; foreach ($shapeRow->getCells() as $keyCell => $shapeCell) { if ($numColspan == 0) { @@ -585,10 +649,10 @@ public function writeShapeTable(XMLWriter $objWriter, Table $shape) $objWriter->writeAttribute('table:number-columns-spanned', $shapeCell->getColspan()); $numColspan = $shapeCell->getColspan() - 1; } - + // text:p $objWriter->startElement('text:p'); - + // text:span foreach ($shapeCell->getParagraphs() as $shapeParagraph) { foreach ($shapeParagraph->getRichTextElements() as $shapeRichText) { @@ -596,7 +660,7 @@ public function writeShapeTable(XMLWriter $objWriter, Table $shape) // text:span $objWriter->startElement('text:span'); if ($shapeRichText instanceof Run) { - $objWriter->writeAttribute('text:style-name', 'T_' . $shapeRichText->getFont()->getHashCode()); + $objWriter->writeAttribute('text:style-name', 'T_' . $shapeRichText->getHashCode()); } if ($shapeRichText->hasHyperlink() === true && $shapeRichText->getHyperlink()->getUrl() != '') { // text:a @@ -618,10 +682,10 @@ public function writeShapeTable(XMLWriter $objWriter, Table $shape) } } } - + // > text:p $objWriter->endElement(); - + // > table:table-cell $objWriter->endElement(); } else { @@ -638,20 +702,19 @@ public function writeShapeTable(XMLWriter $objWriter, Table $shape) // > draw:frame $objWriter->endElement(); } - + /** * Write table Chart * @param XMLWriter $objWriter * @param Chart $shape + * @throws \Exception */ public function writeShapeChart(XMLWriter $objWriter, Chart $shape) { - $parentWriter = $this->getParentWriter(); - if (!$parentWriter instanceof ODPresentation) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\ODPresentation'); - } - $parentWriter->chartArray[$this->shapeId] = $shape; - + $arrayChart = $this->getArrayChart(); + $arrayChart[$this->shapeId] = $shape; + $this->setArrayChart($arrayChart); + // draw:frame $objWriter->startElement('draw:frame'); $objWriter->writeAttribute('draw:name', $shape->getTitle()->getText()); @@ -659,13 +722,13 @@ public function writeShapeChart(XMLWriter $objWriter, Chart $shape) $objWriter->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetY()), 3) . 'cm'); $objWriter->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getHeight()), 3) . 'cm'); $objWriter->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getWidth()), 3) . 'cm'); - + // draw:object $objWriter->startElement('draw:object'); $objWriter->writeAttribute('xlink:href', './Object '.$this->shapeId); $objWriter->writeAttribute('xlink:type', 'simple'); $objWriter->writeAttribute('xlink:show', 'embed'); - + // > draw:object $objWriter->endElement(); // > draw:frame @@ -697,8 +760,8 @@ public function writeShapeGroup(XMLWriter $objWriter, Group $group) $this->writeShapeLine($objWriter, $shape); } elseif ($shape instanceof Chart) { $this->writeShapeChart($objWriter, $shape); - } elseif ($shape instanceof AbstractDrawing) { - $this->writeShapePic($objWriter, $shape); + } elseif ($shape instanceof ShapeDrawing\AbstractDrawingAdapter) { + $this->writeShapeDrawing($objWriter, $shape); } elseif ($shape instanceof Group) { $this->writeShapeGroup($objWriter, $shape); } @@ -823,13 +886,14 @@ public function writeTxtStyle(XMLWriter $objWriter, RichText $shape) } // Style des listes - if (!isset($this->arrStyleBullet[$paragraph->getBulletStyle()->getHashCode()])) { - $this->arrStyleBullet[$paragraph->getBulletStyle()->getHashCode()]['oStyle'] = $paragraph->getBulletStyle(); - $this->arrStyleBullet[$paragraph->getBulletStyle()->getHashCode()]['level'] = ''; + $bulletStyleHashCode = $paragraph->getBulletStyle()->getHashCode(); + if (!isset($this->arrStyleBullet[$bulletStyleHashCode])) { + $this->arrStyleBullet[$bulletStyleHashCode]['oStyle'] = $paragraph->getBulletStyle(); + $this->arrStyleBullet[$bulletStyleHashCode]['level'] = ''; } - if (strpos($this->arrStyleBullet[$paragraph->getBulletStyle()->getHashCode()]['level'], ';' . $paragraph->getAlignment()->getLevel()) === false) { - $this->arrStyleBullet[$paragraph->getBulletStyle()->getHashCode()]['level'] .= ';' . $paragraph->getAlignment()->getLevel(); - $this->arrStyleBullet[$paragraph->getBulletStyle()->getHashCode()]['oAlign_' . $paragraph->getAlignment()->getLevel()] = $paragraph->getAlignment(); + if (strpos($this->arrStyleBullet[$bulletStyleHashCode]['level'], ';' . $paragraph->getAlignment()->getLevel()) === false) { + $this->arrStyleBullet[$bulletStyleHashCode]['level'] .= ';' . $paragraph->getAlignment()->getLevel(); + $this->arrStyleBullet[$bulletStyleHashCode]['oAlign_' . $paragraph->getAlignment()->getLevel()] = $paragraph->getAlignment(); } $richtexts = $paragraph->getRichTextElements(); @@ -839,8 +903,8 @@ public function writeTxtStyle(XMLWriter $objWriter, RichText $shape) // Not a line break if ($richtext instanceof Run) { // Style des font text - if (!isset($this->arrStyleTextFont[$richtext->getFont()->getHashCode()])) { - $this->arrStyleTextFont[$richtext->getFont()->getHashCode()] = $richtext->getFont(); + if (!isset($this->arrStyleTextFont[$richtext->getHashCode()])) { + $this->arrStyleTextFont[$richtext->getHashCode()] = $richtext; } } } @@ -950,49 +1014,50 @@ public function writeTableStyle(XMLWriter $objWriter, Table $shape) // style:paragraph-properties $objWriter->startElement('style:paragraph-properties'); - if ($shapeCell->getBorders()->getBottom()->getHashCode() == $shapeCell->getBorders()->getTop()->getHashCode() - && $shapeCell->getBorders()->getBottom()->getHashCode() == $shapeCell->getBorders()->getLeft()->getHashCode() - && $shapeCell->getBorders()->getBottom()->getHashCode() == $shapeCell->getBorders()->getRight()->getHashCode()) { + $cellBorders = $shapeCell->getBorders(); + if ($cellBorders->getBottom()->getHashCode() == $cellBorders->getTop()->getHashCode() + && $cellBorders->getBottom()->getHashCode() == $cellBorders->getLeft()->getHashCode() + && $cellBorders->getBottom()->getHashCode() == $cellBorders->getRight()->getHashCode()) { $lineStyle = 'none'; - $lineWidth = Text::numberFormat($shapeCell->getBorders()->getBottom()->getLineWidth() / 1.75, 2); - $lineColor = $shapeCell->getBorders()->getBottom()->getColor()->getRGB(); - switch ($shapeCell->getBorders()->getBottom()->getLineStyle()) { + $lineWidth = Text::numberFormat($cellBorders->getBottom()->getLineWidth() / 1.75, 2); + $lineColor = $cellBorders->getBottom()->getColor()->getRGB(); + switch ($cellBorders->getBottom()->getLineStyle()) { case Border::LINE_SINGLE: $lineStyle = 'solid'; } $objWriter->writeAttribute('fo:border', $lineWidth.'pt '.$lineStyle.' #'.$lineColor); } else { $lineStyle = 'none'; - $lineWidth = Text::numberFormat($shapeCell->getBorders()->getBottom()->getLineWidth() / 1.75, 2); - $lineColor = $shapeCell->getBorders()->getBottom()->getColor()->getRGB(); - switch ($shapeCell->getBorders()->getBottom()->getLineStyle()) { + $lineWidth = Text::numberFormat($cellBorders->getBottom()->getLineWidth() / 1.75, 2); + $lineColor = $cellBorders->getBottom()->getColor()->getRGB(); + switch ($cellBorders->getBottom()->getLineStyle()) { case Border::LINE_SINGLE: $lineStyle = 'solid'; } $objWriter->writeAttribute('fo:border-bottom', $lineWidth.'pt '.$lineStyle.' #'.$lineColor); // TOP $lineStyle = 'none'; - $lineWidth = Text::numberFormat($shapeCell->getBorders()->getTop()->getLineWidth() / 1.75, 2); - $lineColor = $shapeCell->getBorders()->getTop()->getColor()->getRGB(); - switch ($shapeCell->getBorders()->getTop()->getLineStyle()) { + $lineWidth = Text::numberFormat($cellBorders->getTop()->getLineWidth() / 1.75, 2); + $lineColor = $cellBorders->getTop()->getColor()->getRGB(); + switch ($cellBorders->getTop()->getLineStyle()) { case Border::LINE_SINGLE: $lineStyle = 'solid'; } $objWriter->writeAttribute('fo:border-top', $lineWidth.'pt '.$lineStyle.' #'.$lineColor); // RIGHT $lineStyle = 'none'; - $lineWidth = Text::numberFormat($shapeCell->getBorders()->getRight()->getLineWidth() / 1.75, 2); - $lineColor = $shapeCell->getBorders()->getRight()->getColor()->getRGB(); - switch ($shapeCell->getBorders()->getRight()->getLineStyle()) { + $lineWidth = Text::numberFormat($cellBorders->getRight()->getLineWidth() / 1.75, 2); + $lineColor = $cellBorders->getRight()->getColor()->getRGB(); + switch ($cellBorders->getRight()->getLineStyle()) { case Border::LINE_SINGLE: $lineStyle = 'solid'; } $objWriter->writeAttribute('fo:border-right', $lineWidth.'pt '.$lineStyle.' #'.$lineColor); // LEFT $lineStyle = 'none'; - $lineWidth = Text::numberFormat($shapeCell->getBorders()->getLeft()->getLineWidth() / 1.75, 2); - $lineColor = $shapeCell->getBorders()->getLeft()->getColor()->getRGB(); - switch ($shapeCell->getBorders()->getLeft()->getLineStyle()) { + $lineWidth = Text::numberFormat($cellBorders->getLeft()->getLineWidth() / 1.75, 2); + $lineColor = $cellBorders->getLeft()->getColor()->getRGB(); + switch ($cellBorders->getLeft()->getLineStyle()) { case Border::LINE_SINGLE: $lineStyle = 'solid'; } @@ -1006,8 +1071,8 @@ public function writeTableStyle(XMLWriter $objWriter, Table $shape) foreach ($shapeParagraph->getRichTextElements() as $shapeRichText) { if ($shapeRichText instanceof Run) { // Style des font text - if (!isset($this->arrStyleTextFont[$shapeRichText->getFont()->getHashCode()])) { - $this->arrStyleTextFont[$shapeRichText->getFont()->getHashCode()] = $shapeRichText->getFont(); + if (!isset($this->arrStyleTextFont[$shapeRichText->getHashCode()])) { + $this->arrStyleTextFont[$shapeRichText->getHashCode()] = $shapeRichText; } } } @@ -1026,16 +1091,16 @@ public function writeSlideNote(XMLWriter $objWriter, Note $note) $shapesNote = $note->getShapeCollection(); if (count($shapesNote) > 0) { $objWriter->startElement('presentation:notes'); - + foreach ($shapesNote as $shape) { // Increment $this->shapeId ++$this->shapeId; - + if ($shape instanceof RichText) { $this->writeShapeTxt($objWriter, $shape); } } - + $objWriter->endElement(); } } @@ -1054,13 +1119,11 @@ public function writeStyleSlide(XMLWriter $objWriter, Slide $slide, $incPage) $objWriter->writeAttribute('style:name', 'stylePage'.$incPage); // style:style/style:drawing-page-properties $objWriter->startElement('style:drawing-page-properties'); + $objWriter->writeAttributeIf(!$slide->isVisible(), 'presentation:visibility', 'hidden'); if (!is_null($oTransition = $slide->getTransition())) { $objWriter->writeAttribute('presentation:duration', 'PT'.number_format($oTransition->getAdvanceTimeTrigger() / 1000, 6, '.', '').'S'); - if ($oTransition->hasManualTrigger()) { - $objWriter->writeAttribute('presentation:transition-type', 'manual'); - } elseif ($oTransition->hasTimeTrigger()) { - $objWriter->writeAttribute('presentation:transition-type', 'automatic'); - } + $objWriter->writeAttributeIf($oTransition->hasManualTrigger(), 'presentation:transition-type', 'manual'); + $objWriter->writeAttributeIf($oTransition->hasTimeTrigger(), 'presentation:transition-type', 'automatic'); switch ($oTransition->getSpeed()) { case Transition::SPEED_FAST: $objWriter->writeAttribute('presentation:transition-speed', 'fast'); @@ -1240,7 +1303,7 @@ public function writeStyleSlide(XMLWriter $objWriter, Slide $slide, $incPage) // > style:style $objWriter->endElement(); } - + /** * @param XMLWriter $objWriter @@ -1251,33 +1314,34 @@ protected function writeStylePartShadow(XMLWriter $objWriter, Shadow $oShadow) { $objWriter->writeAttribute('draw:shadow', 'visible'); $objWriter->writeAttribute('draw:shadow-color', '#' . $oShadow->getColor()->getRGB()); + + $distanceCms = CommonDrawing::pixelsToCentimeters($oShadow->getDistance()); if ($oShadow->getDirection() == 0 || $oShadow->getDirection() == 360) { - $objWriter->writeAttribute('draw:shadow-offset-x', CommonDrawing::pixelsToCentimeters($oShadow->getDistance()) . 'cm'); + $objWriter->writeAttribute('draw:shadow-offset-x', $distanceCms . 'cm'); $objWriter->writeAttribute('draw:shadow-offset-y', '0cm'); } elseif ($oShadow->getDirection() == 45) { - $objWriter->writeAttribute('draw:shadow-offset-x', CommonDrawing::pixelsToCentimeters($oShadow->getDistance()) . 'cm'); - $objWriter->writeAttribute('draw:shadow-offset-y', CommonDrawing::pixelsToCentimeters($oShadow->getDistance()) . 'cm'); + $objWriter->writeAttribute('draw:shadow-offset-x', $distanceCms . 'cm'); + $objWriter->writeAttribute('draw:shadow-offset-y', $distanceCms . 'cm'); } elseif ($oShadow->getDirection() == 90) { $objWriter->writeAttribute('draw:shadow-offset-x', '0cm'); - $objWriter->writeAttribute('draw:shadow-offset-y', CommonDrawing::pixelsToCentimeters($oShadow->getDistance()) . 'cm'); + $objWriter->writeAttribute('draw:shadow-offset-y', $distanceCms . 'cm'); } elseif ($oShadow->getDirection() == 135) { - $objWriter->writeAttribute('draw:shadow-offset-x', '-' . CommonDrawing::pixelsToCentimeters($oShadow->getDistance()) . 'cm'); - $objWriter->writeAttribute('draw:shadow-offset-y', CommonDrawing::pixelsToCentimeters($oShadow->getDistance()) . 'cm'); + $objWriter->writeAttribute('draw:shadow-offset-x', '-' . $distanceCms . 'cm'); + $objWriter->writeAttribute('draw:shadow-offset-y', $distanceCms . 'cm'); } elseif ($oShadow->getDirection() == 180) { - $objWriter->writeAttribute('draw:shadow-offset-x', '-' . CommonDrawing::pixelsToCentimeters($oShadow->getDistance()) . 'cm'); + $objWriter->writeAttribute('draw:shadow-offset-x', '-' . $distanceCms . 'cm'); $objWriter->writeAttribute('draw:shadow-offset-y', '0cm'); } elseif ($oShadow->getDirection() == 225) { - $objWriter->writeAttribute('draw:shadow-offset-x', '-' . CommonDrawing::pixelsToCentimeters($oShadow->getDistance()) . 'cm'); - $objWriter->writeAttribute('draw:shadow-offset-y', '-' . CommonDrawing::pixelsToCentimeters($oShadow->getDistance()) . 'cm'); + $objWriter->writeAttribute('draw:shadow-offset-x', '-' . $distanceCms . 'cm'); + $objWriter->writeAttribute('draw:shadow-offset-y', '-' . $distanceCms . 'cm'); } elseif ($oShadow->getDirection() == 270) { $objWriter->writeAttribute('draw:shadow-offset-x', '0cm'); - $objWriter->writeAttribute('draw:shadow-offset-y', '-' . CommonDrawing::pixelsToCentimeters($oShadow->getDistance()) . 'cm'); + $objWriter->writeAttribute('draw:shadow-offset-y', '-' . $distanceCms . 'cm'); } elseif ($oShadow->getDirection() == 315) { - $objWriter->writeAttribute('draw:shadow-offset-x', CommonDrawing::pixelsToCentimeters($oShadow->getDistance()) . 'cm'); - $objWriter->writeAttribute('draw:shadow-offset-y', '-' . CommonDrawing::pixelsToCentimeters($oShadow->getDistance()) . 'cm'); + $objWriter->writeAttribute('draw:shadow-offset-x', $distanceCms . 'cm'); + $objWriter->writeAttribute('draw:shadow-offset-y', '-' . $distanceCms . 'cm'); } $objWriter->writeAttribute('draw:shadow-opacity', (100 - $oShadow->getAlpha()) . '%'); $objWriter->writeAttribute('style:mirror', 'none'); - } } diff --git a/src/PhpPresentation/Writer/ODPresentation/Drawing.php b/src/PhpPresentation/Writer/ODPresentation/Drawing.php deleted file mode 100644 index 1f9fc003b..000000000 --- a/src/PhpPresentation/Writer/ODPresentation/Drawing.php +++ /dev/null @@ -1,66 +0,0 @@ -getSlideCount(); - for ($i = 0; $i < $slideCount; ++$i) { - // Loop trough images and add to array - $iterator = $pPhpPresentation->getSlide($i)->getShapeCollection()->getIterator(); - while ($iterator->valid()) { - if ($iterator->current() instanceof AbstractDrawing && !($iterator->current() instanceof Table)) { - $aDrawings[] = $iterator->current(); - } elseif ($iterator->current() instanceof Group) { - $iterator2 = $iterator->current()->getShapeCollection()->getIterator(); - while ($iterator2->valid()) { - if ($iterator2->current() instanceof AbstractDrawing && !($iterator2->current() instanceof Table)) { - $aDrawings[] = $iterator2->current(); - } - $iterator2->next(); - } - } - - $iterator->next(); - } - } - - return $aDrawings; - } -} diff --git a/src/PhpPresentation/Writer/ODPresentation/Manifest.php b/src/PhpPresentation/Writer/ODPresentation/Manifest.php deleted file mode 100644 index 585a048cf..000000000 --- a/src/PhpPresentation/Writer/ODPresentation/Manifest.php +++ /dev/null @@ -1,160 +0,0 @@ -getParentWriter(); - if (!$parentWriter instanceof ODPresentation) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\ODPresentation'); - } - - // Create XML writer - $objWriter = $this->getXMLWriter(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8'); - - // manifest:manifest - $objWriter->startElement('manifest:manifest'); - $objWriter->writeAttribute('xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'); - $objWriter->writeAttribute('manifest:version', '1.2'); - - // manifest:file-entry - $objWriter->startElement('manifest:file-entry'); - $objWriter->writeAttribute('manifest:media-type', 'application/vnd.oasis.opendocument.presentation'); - $objWriter->writeAttribute('manifest:version', '1.2'); - $objWriter->writeAttribute('manifest:full-path', '/'); - $objWriter->endElement(); - // manifest:file-entry - $objWriter->startElement('manifest:file-entry'); - $objWriter->writeAttribute('manifest:media-type', 'text/xml'); - $objWriter->writeAttribute('manifest:full-path', 'content.xml'); - $objWriter->endElement(); - // manifest:file-entry - $objWriter->startElement('manifest:file-entry'); - $objWriter->writeAttribute('manifest:media-type', 'text/xml'); - $objWriter->writeAttribute('manifest:full-path', 'meta.xml'); - $objWriter->endElement(); - // manifest:file-entry - $objWriter->startElement('manifest:file-entry'); - $objWriter->writeAttribute('manifest:media-type', 'text/xml'); - $objWriter->writeAttribute('manifest:full-path', 'styles.xml'); - $objWriter->endElement(); - - // Charts - foreach ($parentWriter->chartArray as $key => $shape) { - $objWriter->startElement('manifest:file-entry'); - $objWriter->writeAttribute('manifest:full-path', 'Object '.$key.'/'); - $objWriter->writeAttribute('manifest:media-type', 'application/vnd.oasis.opendocument.chart'); - $objWriter->endElement(); - $objWriter->startElement('manifest:file-entry'); - $objWriter->writeAttribute('manifest:full-path', 'Object '.$key.'/content.xml'); - $objWriter->writeAttribute('manifest:media-type', 'text/xml'); - $objWriter->endElement(); - } - - $arrMedia = array(); - for ($i = 0; $i < $parentWriter->getDrawingHashTable()->count(); ++$i) { - $shape = $parentWriter->getDrawingHashTable()->getByIndex($i); - if ($shape instanceof ShapeDrawing) { - if (!in_array(md5($shape->getPath()), $arrMedia)) { - $arrMedia[] = md5($shape->getPath()); - $mimeType = $this->getImageMimeType($shape->getPath()); - - $objWriter->startElement('manifest:file-entry'); - $objWriter->writeAttribute('manifest:media-type', $mimeType); - $objWriter->writeAttribute('manifest:full-path', 'Pictures/' . md5($shape->getPath()) . '.' . $shape->getExtension()); - $objWriter->endElement(); - } - } elseif ($shape instanceof MemoryDrawing) { - if (!in_array(str_replace(' ', '_', $shape->getIndexedFilename()), $arrMedia)) { - $arrMedia[] = str_replace(' ', '_', $shape->getIndexedFilename()); - $mimeType = $shape->getMimeType(); - - $objWriter->startElement('manifest:file-entry'); - $objWriter->writeAttribute('manifest:media-type', $mimeType); - $objWriter->writeAttribute('manifest:full-path', 'Pictures/' . str_replace(' ', '_', $shape->getIndexedFilename())); - $objWriter->endElement(); - } - } - } - - foreach ($parentWriter->getPhpPresentation()->getAllSlides() as $numSlide => $oSlide) { - $oBkgImage = $oSlide->getBackground(); - if ($oBkgImage instanceof Image) { - $mimeType = $this->getImageMimeType($oBkgImage->getPath()); - - $objWriter->startElement('manifest:file-entry'); - $objWriter->writeAttribute('manifest:media-type', $mimeType); - $objWriter->writeAttribute('manifest:full-path', 'Pictures/' . str_replace(' ', '_', $oBkgImage->getIndexedFilename($numSlide))); - $objWriter->endElement(); - } - } - - $objWriter->endElement(); - - // Return - return $objWriter->getData(); - } - - /** - * Get image mime type - * - * @param string $pFile Filename - * @return string Mime Type - * @throws \Exception - */ - private function getImageMimeType($pFile = '') - { - if (File::fileExists($pFile)) { - if (strpos($pFile, 'zip://') === 0) { - $pZIPFile = str_replace('zip://', '', $pFile); - $pZIPFile = substr($pZIPFile, 0, strpos($pZIPFile, '#')); - $pImgFile = substr($pFile, strpos($pFile, '#') + 1); - $oArchive = new \ZipArchive(); - $oArchive->open($pZIPFile); - $image = getimagesizefromstring($oArchive->getFromName($pImgFile)); - } else { - $image = getimagesize($pFile); - } - - return image_type_to_mime_type($image[2]); - } else { - throw new \Exception("File $pFile does not exist"); - } - } -} diff --git a/src/PhpPresentation/Writer/ODPresentation/Meta.php b/src/PhpPresentation/Writer/ODPresentation/Meta.php index 75fe0e463..b73dfae21 100644 --- a/src/PhpPresentation/Writer/ODPresentation/Meta.php +++ b/src/PhpPresentation/Writer/ODPresentation/Meta.php @@ -1,42 +1,18 @@ getXMLWriter(); - - // XML header + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); $objWriter->startDocument('1.0', 'UTF-8'); // office:document-meta @@ -58,31 +34,31 @@ public function writePart(PhpPresentation $pPhpPresentation) $objWriter->startElement('office:meta'); // dc:creator - $objWriter->writeElement('dc:creator', $pPhpPresentation->getProperties()->getLastModifiedBy()); + $objWriter->writeElement('dc:creator', $this->getPresentation()->getDocumentProperties()->getLastModifiedBy()); // dc:date - $objWriter->writeElement('dc:date', gmdate('Y-m-d\TH:i:s.000', $pPhpPresentation->getProperties()->getModified())); + $objWriter->writeElement('dc:date', gmdate('Y-m-d\TH:i:s.000', $this->getPresentation()->getDocumentProperties()->getModified())); // dc:description - $objWriter->writeElement('dc:description', $pPhpPresentation->getProperties()->getDescription()); + $objWriter->writeElement('dc:description', $this->getPresentation()->getDocumentProperties()->getDescription()); // dc:subject - $objWriter->writeElement('dc:subject', $pPhpPresentation->getProperties()->getSubject()); + $objWriter->writeElement('dc:subject', $this->getPresentation()->getDocumentProperties()->getSubject()); // dc:title - $objWriter->writeElement('dc:title', $pPhpPresentation->getProperties()->getTitle()); + $objWriter->writeElement('dc:title', $this->getPresentation()->getDocumentProperties()->getTitle()); // meta:creation-date - $objWriter->writeElement('meta:creation-date', gmdate('Y-m-d\TH:i:s.000', $pPhpPresentation->getProperties()->getCreated())); + $objWriter->writeElement('meta:creation-date', gmdate('Y-m-d\TH:i:s.000', $this->getPresentation()->getDocumentProperties()->getCreated())); // meta:initial-creator - $objWriter->writeElement('meta:initial-creator', $pPhpPresentation->getProperties()->getCreator()); + $objWriter->writeElement('meta:initial-creator', $this->getPresentation()->getDocumentProperties()->getCreator()); // meta:keyword - $objWriter->writeElement('meta:keyword', $pPhpPresentation->getProperties()->getKeywords()); + $objWriter->writeElement('meta:keyword', $this->getPresentation()->getDocumentProperties()->getKeywords()); // @todo : Where these properties are written ? - // $pPhpPresentation->getProperties()->getCategory() - // $pPhpPresentation->getProperties()->getCompany() + // $this->getPresentation()->getDocumentProperties()->getCategory() + // $this->getPresentation()->getDocumentProperties()->getCompany() $objWriter->endElement(); $objWriter->endElement(); - - // Return - return $objWriter->getData(); + + $this->getZip()->addFromString('meta.xml', $objWriter->getData()); + return $this->getZip(); } } diff --git a/src/PhpPresentation/Writer/ODPresentation/MetaInfManifest.php b/src/PhpPresentation/Writer/ODPresentation/MetaInfManifest.php new file mode 100644 index 000000000..1a2710a9b --- /dev/null +++ b/src/PhpPresentation/Writer/ODPresentation/MetaInfManifest.php @@ -0,0 +1,108 @@ +startDocument('1.0', 'UTF-8'); + + // manifest:manifest + $objWriter->startElement('manifest:manifest'); + $objWriter->writeAttribute('xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'); + $objWriter->writeAttribute('manifest:version', '1.2'); + + // manifest:file-entry + $objWriter->startElement('manifest:file-entry'); + $objWriter->writeAttribute('manifest:media-type', 'application/vnd.oasis.opendocument.presentation'); + $objWriter->writeAttribute('manifest:full-path', '/'); + $objWriter->writeAttribute('manifest:version', '1.2'); + $objWriter->endElement(); + // manifest:file-entry + $objWriter->startElement('manifest:file-entry'); + $objWriter->writeAttribute('manifest:media-type', 'text/xml'); + $objWriter->writeAttribute('manifest:full-path', 'content.xml'); + $objWriter->endElement(); + // manifest:file-entry + $objWriter->startElement('manifest:file-entry'); + $objWriter->writeAttribute('manifest:media-type', 'text/xml'); + $objWriter->writeAttribute('manifest:full-path', 'meta.xml'); + $objWriter->endElement(); + // manifest:file-entry + $objWriter->startElement('manifest:file-entry'); + $objWriter->writeAttribute('manifest:media-type', 'text/xml'); + $objWriter->writeAttribute('manifest:full-path', 'styles.xml'); + $objWriter->endElement(); + + // Charts + foreach ($this->getArrayChart() as $key => $shape) { + $objWriter->startElement('manifest:file-entry'); + $objWriter->writeAttribute('manifest:media-type', 'application/vnd.oasis.opendocument.chart'); + $objWriter->writeAttribute('manifest:full-path', 'Object '.$key.'/'); + $objWriter->endElement(); + $objWriter->startElement('manifest:file-entry'); + $objWriter->writeAttribute('manifest:media-type', 'text/xml'); + $objWriter->writeAttribute('manifest:full-path', 'Object '.$key.'/content.xml'); + $objWriter->endElement(); + } + + $arrMedia = array(); + for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) { + $shape = $this->getDrawingHashTable()->getByIndex($i); + if (! ($shape instanceof ShapeDrawing\AbstractDrawingAdapter)) { + continue; + } + $arrMedia[] = $shape->getIndexedFilename(); + $objWriter->startElement('manifest:file-entry'); + $objWriter->writeAttribute('manifest:media-type', $shape->getMimeType()); + $objWriter->writeAttribute('manifest:full-path', 'Pictures/' . $shape->getIndexedFilename()); + $objWriter->endElement(); + } + + foreach ($this->getPresentation()->getAllSlides() as $numSlide => $oSlide) { + $oBkgImage = $oSlide->getBackground(); + if ($oBkgImage instanceof Image) { + $arrayImage = getimagesize($oBkgImage->getPath()); + $mimeType = image_type_to_mime_type($arrayImage[2]); + + $objWriter->startElement('manifest:file-entry'); + $objWriter->writeAttribute('manifest:media-type', $mimeType); + $objWriter->writeAttribute('manifest:full-path', 'Pictures/' . str_replace(' ', '_', $oBkgImage->getIndexedFilename($numSlide))); + $objWriter->endElement(); + } + } + + if ($this->getPresentation()->getPresentationProperties()->getThumbnailPath()) { + $pathThumbnail = $this->getPresentation()->getPresentationProperties()->getThumbnailPath(); + // Size : 128x128 pixel + // PNG : 8bit, non-interlaced with full alpha transparency + $gdImage = imagecreatefromstring(file_get_contents($pathThumbnail)); + if ($gdImage) { + imagedestroy($gdImage); + $objWriter->startElement('manifest:file-entry'); + $objWriter->writeAttribute('manifest:media-type', 'image/png'); + $objWriter->writeAttribute('manifest:full-path', 'Thumbnails/thumbnail.png'); + $objWriter->endElement(); + } + } + + $objWriter->endElement(); + + $this->getZip()->addFromString('META-INF/manifest.xml', $objWriter->getData()); + return $this->getZip(); + } +} diff --git a/src/PhpPresentation/Writer/ODPresentation/Mimetype.php b/src/PhpPresentation/Writer/ODPresentation/Mimetype.php index 16a9fb9ec..342da946d 100644 --- a/src/PhpPresentation/Writer/ODPresentation/Mimetype.php +++ b/src/PhpPresentation/Writer/ODPresentation/Mimetype.php @@ -1,37 +1,15 @@ getZip()->addFromString('mimetype', 'application/vnd.oasis.opendocument.presentation'); + return $this->getZip(); } } diff --git a/src/PhpPresentation/Writer/ODPresentation/ObjectsChart.php b/src/PhpPresentation/Writer/ODPresentation/ObjectsChart.php index 64a2a1cc8..3403623d6 100644 --- a/src/PhpPresentation/Writer/ODPresentation/ObjectsChart.php +++ b/src/PhpPresentation/Writer/ODPresentation/ObjectsChart.php @@ -1,26 +1,11 @@ xmlContent = $this->getXMLWriter(); - $this->xmlMeta = $this->getXMLWriter(); - $this->xmlStyles = $this->getXMLWriter(); - - $this->writeContentPart($chart); - - return array( - 'content.xml' => $this->xmlContent->getData(), - 'meta.xml' => $this->xmlMeta->getData(), - 'styles.xml' => $this->xmlStyles->getData(), - ); + foreach ($this->getArrayChart() as $keyChart => $shapeChart) { + $content = $this->writeContentPart($shapeChart); + + if (!empty($content)) { + $this->getZip()->addFromString('Object '.$keyChart.'/content.xml', $content); + } + } + + return $this->getZip(); } - + /** * @param Chart $chart + * @return string + * @throws \Exception */ - private function writeContentPart(Chart $chart) + protected function writeContentPart(Chart $chart) { + $this->xmlContent = new XMLWriter(XMLWriter::STORAGE_MEMORY); + $chartType = $chart->getPlotArea()->getType(); - if (!($chartType instanceof Area || $chartType instanceof AbstractTypeBar || $chartType instanceof Line || $chartType instanceof AbstractTypePie || $chartType instanceof Scatter)) { - throw new \Exception('The chart type provided could not be rendered.'); - } - + // Data $this->arrayData = array(); $this->arrayTitle = array(); $this->numData = 0; - foreach ($chart->getPlotArea()->getType()->getData() as $series) { + foreach ($chartType->getSeries() as $series) { $inc = 0; $this->arrayTitle[] = $series->getTitle(); foreach ($series->getValues() as $key => $value) { @@ -118,7 +94,7 @@ private function writeContentPart(Chart $chart) $this->numData = $inc; } } - + // office:document-content $this->xmlContent->startElement('office:document-content'); $this->xmlContent->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'); @@ -157,42 +133,42 @@ private function writeContentPart(Chart $chart) $this->xmlContent->writeAttribute('xmlns:formx', 'urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0'); $this->xmlContent->writeAttribute('xmlns:css3t', 'http://www.w3.org/TR/css3-text/'); $this->xmlContent->writeAttribute('office:version', '1.2'); - + // office:automatic-styles $this->xmlContent->startElement('office:automatic-styles'); - + // Chart $this->writeChartStyle($chart); - + // Axis $this->writeAxisStyle($chart); - + // Series $this->numSeries = 0; - foreach ($chart->getPlotArea()->getType()->getData() as $series) { + foreach ($chartType->getSeries() as $series) { $this->writeSeriesStyle($chart, $series); - + $this->numSeries++; } - + // Floor $this->writeFloorStyle(); - + // Legend $this->writeLegendStyle($chart); - + // PlotArea $this->writePlotAreaStyle($chart); - + // Title $this->writeTitleStyle($chart->getTitle()); - + // Wall $this->writeWallStyle($chart); - + // > office:automatic-styles $this->xmlContent->endElement(); - + // office:body $this->xmlContent->startElement('office:body'); // office:chart @@ -204,30 +180,24 @@ private function writeContentPart(Chart $chart) $this->xmlContent->writeAttribute('xlink:href', '.'); $this->xmlContent->writeAttribute('xlink:type', 'simple'); $this->xmlContent->writeAttribute('chart:style-name', 'styleChart'); - if ($chartType instanceof Area) { - $this->xmlContent->writeAttribute('chart:class', 'chart:area'); - } elseif ($chartType instanceof AbstractTypeBar) { - $this->xmlContent->writeAttribute('chart:class', 'chart:bar'); - } elseif ($chartType instanceof Line) { - $this->xmlContent->writeAttribute('chart:class', 'chart:line'); - } elseif ($chartType instanceof AbstractTypePie) { - $this->xmlContent->writeAttribute('chart:class', 'chart:circle'); - } elseif ($chartType instanceof Scatter) { - $this->xmlContent->writeAttribute('chart:class', 'chart:scatter'); - } - + $this->xmlContent->writeAttributeIf($chartType instanceof Area, 'chart:class', 'chart:area'); + $this->xmlContent->writeAttributeIf($chartType instanceof AbstractTypeBar, 'chart:class', 'chart:bar'); + $this->xmlContent->writeAttributeIf($chartType instanceof Line, 'chart:class', 'chart:line'); + $this->xmlContent->writeAttributeIf($chartType instanceof AbstractTypePie, 'chart:class', 'chart:circle'); + $this->xmlContent->writeAttributeIf($chartType instanceof Scatter, 'chart:class', 'chart:scatter'); + //**** Title **** $this->writeTitle($chart->getTitle()); //**** Legend **** $this->writeLegend($chart); - + //**** Plotarea **** $this->writePlotArea($chart); //**** Table **** $this->writeTable(); - + // > chart:chart $this->xmlContent->endElement(); // > office:chart @@ -236,35 +206,46 @@ private function writeContentPart(Chart $chart) $this->xmlContent->endElement(); // > office:document-content $this->xmlContent->endElement(); + + return $this->xmlContent->getData(); } - + /** * @param Chart $chart */ private function writeAxis(Chart $chart) { $chartType = $chart->getPlotArea()->getType(); - + // chart:axis $this->xmlContent->startElement('chart:axis'); $this->xmlContent->writeAttribute('chart:dimension', 'x'); $this->xmlContent->writeAttribute('chart:name', 'primary-x'); $this->xmlContent->writeAttribute('chartooo:axis-type', 'text'); $this->xmlContent->writeAttribute('chart:style-name', 'styleAxisX'); - // chart:categories + // chart:axis > chart:categories $this->xmlContent->startElement('chart:categories'); $this->xmlContent->writeAttribute('table:cell-range-address', 'table-local.$A$2:.$A$'.($this->numData+1)); - // > chart:categories $this->xmlContent->endElement(); - // > chart:axis + // chart:axis > chart:grid + $this->writeGridline($chart->getPlotArea()->getAxisX()->getMajorGridlines(), 'styleAxisXGridlinesMajor', 'major'); + // chart:axis > chart:grid + $this->writeGridline($chart->getPlotArea()->getAxisX()->getMinorGridlines(), 'styleAxisXGridlinesMinor', 'minor'); + // ##chart:axis $this->xmlContent->endElement(); + // chart:axis $this->xmlContent->startElement('chart:axis'); $this->xmlContent->writeAttribute('chart:dimension', 'y'); $this->xmlContent->writeAttribute('chart:name', 'primary-y'); $this->xmlContent->writeAttribute('chart:style-name', 'styleAxisY'); - // > chart:axis + // chart:axis > chart:grid + $this->writeGridline($chart->getPlotArea()->getAxisY()->getMajorGridlines(), 'styleAxisYGridlinesMajor', 'major'); + // chart:axis > chart:grid + $this->writeGridline($chart->getPlotArea()->getAxisY()->getMinorGridlines(), 'styleAxisYGridlinesMinor', 'minor'); + // ##chart:axis $this->xmlContent->endElement(); + if ($chartType instanceof Bar3D || $chartType instanceof Pie3D) { // chart:axis $this->xmlContent->startElement('chart:axis'); @@ -274,21 +255,33 @@ private function writeAxis(Chart $chart) $this->xmlContent->endElement(); } } - + + protected function writeGridline($oGridlines, $styleName, $chartClass) + { + if (!($oGridlines instanceof Chart\Gridlines)) { + return ; + } + + $this->xmlContent->startElement('chart:grid'); + $this->xmlContent->writeAttribute('chart:style-name', $styleName); + $this->xmlContent->writeAttribute('chart:class', $chartClass); + $this->xmlContent->endElement(); + } + /** * @param Chart $chart * @todo Set function in \PhpPresentation\Shape\Chart\Axis for defining width and color of the axis */ - private function writeAxisStyle(Chart $chart) + protected function writeAxisStyle(Chart $chart) { $chartType = $chart->getPlotArea()->getType(); - + // AxisX // style:style $this->xmlContent->startElement('style:style'); $this->xmlContent->writeAttribute('style:name', 'styleAxisX'); $this->xmlContent->writeAttribute('style:family', 'chart'); - // style:chart-properties + // style:style > style:chart-properties $this->xmlContent->startElement('style:chart-properties'); $this->xmlContent->writeAttribute('chart:display-label', 'true'); $this->xmlContent->writeAttribute('chart:tick-marks-major-inner', 'false'); @@ -296,23 +289,35 @@ private function writeAxisStyle(Chart $chart) if ($chartType instanceof AbstractTypePie) { $this->xmlContent->writeAttribute('chart:reverse-direction', 'true'); } - // > style:chart-properties $this->xmlContent->endElement(); - // style:graphic-properties + // style:style > style:text-properties + $oFont = $chart->getPlotArea()->getAxisX()->getFont(); + $this->xmlContent->startElement('style:text-properties'); + $this->xmlContent->writeAttribute('fo:color', '#'.$oFont->getColor()->getRGB()); + $this->xmlContent->writeAttribute('fo:font-family', $oFont->getName()); + $this->xmlContent->writeAttribute('fo:font-size', $oFont->getSize().'pt'); + $this->xmlContent->writeAttribute('fo:font-style', $oFont->isItalic() ? 'italic' : 'normal'); + $this->xmlContent->endElement(); + // style:style > style:graphic-properties $this->xmlContent->startElement('style:graphic-properties'); $this->xmlContent->writeAttribute('svg:stroke-width', '0.026cm'); $this->xmlContent->writeAttribute('svg:stroke-color', '#878787'); - // > style:graphic-properties $this->xmlContent->endElement(); - // > style:style + // ##style:style $this->xmlContent->endElement(); - + + // AxisX GridLines Major + $this->writeGridlineStyle($chart->getPlotArea()->getAxisX()->getMajorGridlines(), 'styleAxisXGridlinesMajor'); + + // AxisX GridLines Minor + $this->writeGridlineStyle($chart->getPlotArea()->getAxisX()->getMinorGridlines(), 'styleAxisXGridlinesMinor'); + // AxisY // style:style $this->xmlContent->startElement('style:style'); $this->xmlContent->writeAttribute('style:name', 'styleAxisY'); $this->xmlContent->writeAttribute('style:family', 'chart'); - // style:chart-properties + // style:style > style:chart-properties $this->xmlContent->startElement('style:chart-properties'); $this->xmlContent->writeAttribute('chart:display-label', 'true'); $this->xmlContent->writeAttribute('chart:tick-marks-major-inner', 'false'); @@ -320,18 +325,52 @@ private function writeAxisStyle(Chart $chart) if ($chartType instanceof AbstractTypePie) { $this->xmlContent->writeAttribute('chart:reverse-direction', 'true'); } - // > style:chart-properties + $this->xmlContent->endElement(); + // style:style > style:text-properties + $oFont = $chart->getPlotArea()->getAxisY()->getFont(); + $this->xmlContent->startElement('style:text-properties'); + $this->xmlContent->writeAttribute('fo:color', '#'.$oFont->getColor()->getRGB()); + $this->xmlContent->writeAttribute('fo:font-family', $oFont->getName()); + $this->xmlContent->writeAttribute('fo:font-size', $oFont->getSize().'pt'); + $this->xmlContent->writeAttribute('fo:font-style', $oFont->isItalic() ? 'italic' : 'normal'); $this->xmlContent->endElement(); // style:graphic-properties $this->xmlContent->startElement('style:graphic-properties'); $this->xmlContent->writeAttribute('svg:stroke-width', '0.026cm'); $this->xmlContent->writeAttribute('svg:stroke-color', '#878787'); - // > style:graphic-properties $this->xmlContent->endElement(); - // > style:style + // ## style:style $this->xmlContent->endElement(); + + // AxisY GridLines Major + $this->writeGridlineStyle($chart->getPlotArea()->getAxisY()->getMajorGridlines(), 'styleAxisYGridlinesMajor'); + + // AxisY GridLines Minor + $this->writeGridlineStyle($chart->getPlotArea()->getAxisY()->getMinorGridlines(), 'styleAxisYGridlinesMinor'); } - + + /** + * @param Chart\Gridlines $oGridlines + * @param string $styleName + */ + protected function writeGridlineStyle($oGridlines, $styleName) + { + if (!($oGridlines instanceof Chart\Gridlines)) { + return; + } + // style:style + $this->xmlContent->startElement('style:style'); + $this->xmlContent->writeAttribute('style:name', $styleName); + $this->xmlContent->writeAttribute('style:family', 'chart'); + // style:style > style:graphic-properties + $this->xmlContent->startElement('style:graphic-properties'); + $this->xmlContent->writeAttribute('svg:stroke-width', number_format(CommonDrawing::pointsToCentimeters($oGridlines->getOutline()->getWidth()), 2, '.', '').'cm'); + $this->xmlContent->writeAttribute('svg:stroke-color', '#'.$oGridlines->getOutline()->getFill()->getStartColor()->getRGB()); + $this->xmlContent->endElement(); + // ##style:style + $this->xmlContent->endElement(); + } + /** * @param Chart $chart */ @@ -350,7 +389,7 @@ private function writeChartStyle(Chart $chart) // > style:style $this->xmlContent->endElement(); } - + private function writeFloor() { // chart:floor @@ -359,7 +398,7 @@ private function writeFloor() // > chart:floor $this->xmlContent->endElement(); } - + private function writeFloorStyle() { // style:style @@ -378,7 +417,7 @@ private function writeFloorStyle() // > style:style $this->xmlContent->endElement(); } - + /** * @param Chart $chart */ @@ -394,7 +433,7 @@ private function writeLegend(Chart $chart) // > chart:legend $this->xmlContent->endElement(); } - + /** * @param Chart $chart */ @@ -415,14 +454,14 @@ private function writeLegendStyle(Chart $chart) // > style:style $this->xmlContent->endElement(); } - + /** * @param Chart $chart */ private function writePlotArea(Chart $chart) { $chartType = $chart->getPlotArea()->getType(); - + // chart:plot-area $this->xmlContent->startElement('chart:plot-area'); $this->xmlContent->writeAttribute('chart:style-name', 'stylePlotArea'); @@ -450,19 +489,19 @@ private function writePlotArea(Chart $chart) $this->xmlContent->endElement(); } } - + //**** Axis **** $this->writeAxis($chart); - + //**** Series **** $this->rangeCol = 'B'; $this->numSeries = 0; - foreach ($chart->getPlotArea()->getType()->getData() as $series) { + foreach ($chartType->getSeries() as $series) { $this->writeSeries($chart, $series); $this->rangeCol++; $this->numSeries++; } - + //**** Wall **** $this->writeWall(); //**** Floor **** @@ -470,7 +509,7 @@ private function writePlotArea(Chart $chart) // > chart:plot-area $this->xmlContent->endElement(); } - + /** * @param Chart $chart * @link : http://books.evc-cit.info/odbook/ch08.html#chart-plot-area-section @@ -478,7 +517,7 @@ private function writePlotArea(Chart $chart) private function writePlotAreaStyle(Chart $chart) { $chartType = $chart->getPlotArea()->getType(); - + // style:style $this->xmlContent->startElement('style:style'); $this->xmlContent->writeAttribute('style:name', 'stylePlotArea'); @@ -493,11 +532,11 @@ private function writePlotAreaStyle(Chart $chart) $this->xmlContent->writeAttribute('chart:right-angled-axes', 'true'); } if ($chartType instanceof AbstractTypeBar) { + $chartVertical = 'false'; if ($chartType->getBarDirection() == AbstractTypeBar::DIRECTION_HORIZONTAL) { - $this->xmlContent->writeAttribute('chart:vertical', 'true'); - } else { - $this->xmlContent->writeAttribute('chart:vertical', 'false'); + $chartVertical = 'true'; } + $this->xmlContent->writeAttribute('chart:vertical', $chartVertical); if ($chartType->getBarGrouping() == Bar::GROUPING_CLUSTERED) { $this->xmlContent->writeAttribute('chart:stacked', 'false'); $this->xmlContent->writeAttribute('chart:overlap', '0'); @@ -523,7 +562,7 @@ private function writePlotAreaStyle(Chart $chart) // > style:style $this->xmlContent->endElement(); } - + /** * @param Chart $chart * @param Chart\Series $series @@ -532,7 +571,7 @@ private function writePlotAreaStyle(Chart $chart) private function writeSeries(Chart $chart, Chart\Series $series) { $chartType = $chart->getPlotArea()->getType(); - + $numRange = count($series->getValues()); // chart:series $this->xmlContent->startElement('chart:series'); @@ -552,9 +591,9 @@ private function writeSeries(Chart $chart, Chart\Series $series) $this->xmlContent->writeAttribute('chart:style-name', 'styleSeries'.$this->numSeries); if ($chartType instanceof Area || $chartType instanceof AbstractTypeBar || $chartType instanceof Line || $chartType instanceof Scatter) { $dataPointFills = $series->getDataPointFills(); - if (empty($dataPointFills)) { - $incRepeat = $numRange; - } else { + + $incRepeat = $numRange; + if (!empty($dataPointFills)) { $inc = 0; $incRepeat = 0; $newFill = new Fill(); @@ -566,7 +605,7 @@ private function writeSeries(Chart $chart, Chart\Series $series) // > chart:data-point $this->xmlContent->endElement(); $incRepeat = 0; - + // chart:data-point $this->xmlContent->startElement('chart:data-point'); $this->xmlContent->writeAttribute('chart:style-name', 'styleSeries'.$this->numSeries.'_'.$inc); @@ -593,11 +632,11 @@ private function writeSeries(Chart $chart, Chart\Series $series) $this->xmlContent->endElement(); } } - + // > chart:series $this->xmlContent->endElement(); } - + /** * @param Chart $chart * @param Chart\Series $series @@ -605,7 +644,7 @@ private function writeSeries(Chart $chart, Chart\Series $series) private function writeSeriesStyle(Chart $chart, Chart\Series $series) { $chartType = $chart->getPlotArea()->getType(); - + // style:style $this->xmlContent->startElement('style:style'); $this->xmlContent->writeAttribute('style:name', 'styleSeries'.$this->numSeries); @@ -617,18 +656,61 @@ private function writeSeriesStyle(Chart $chart, Chart\Series $series) if ($chartType instanceof AbstractTypePie) { $this->xmlContent->writeAttribute('chart:pie-offset', $chartType->getExplosion()); } - if ($chartType instanceof Line) { - //@todo : Permit edit the symbol of a line - $this->xmlContent->writeAttribute('chart:symbol-type', 'automatic'); + if ($chartType instanceof Line || $chartType instanceof Scatter) { + $oMarker = $series->getMarker(); + /** + * @link : http://www.datypic.com/sc/odf/a-chart_symbol-type.html + */ + $this->xmlContent->writeAttributeIf($oMarker->getSymbol() == Chart\Marker::SYMBOL_NONE, 'chart:symbol-type', 'none'); + /** + * @link : http://www.datypic.com/sc/odf/a-chart_symbol-name.html + */ + $this->xmlContent->writeAttributeIf($oMarker->getSymbol() != Chart\Marker::SYMBOL_NONE, 'chart:symbol-type', 'named-symbol'); + if ($oMarker->getSymbol() != Chart\Marker::SYMBOL_NONE) { + switch ($oMarker->getSymbol()) { + case Chart\Marker::SYMBOL_DASH: + $symbolName = 'horizontal-bar'; + break; + case Chart\Marker::SYMBOL_DOT: + $symbolName = 'circle'; + break; + case Chart\Marker::SYMBOL_TRIANGLE: + $symbolName = 'arrow-up'; + break; + default: + $symbolName = $oMarker->getSymbol(); + break; + } + $this->xmlContent->writeAttribute('chart:symbol-name', $symbolName); + $symbolSize = number_format(CommonDrawing::pointsToCentimeters($oMarker->getSize()), 2, '.', ''); + $this->xmlContent->writeAttribute('chart:symbol-width', $symbolSize.'cm'); + $this->xmlContent->writeAttribute('chart:symbol-height', $symbolSize.'cm'); + } } // > style:chart-properties $this->xmlContent->endElement(); // style:graphic-properties $this->xmlContent->startElement('style:graphic-properties'); if ($chartType instanceof Line || $chartType instanceof Scatter) { - //@todo : Permit edit the color and width of a line - $this->xmlContent->writeAttribute('svg:stroke-width', '0.079cm'); - $this->xmlContent->writeAttribute('svg:stroke-color', '#4a7ebb'); + $outlineWidth = ''; + $outlineColor = ''; + + $oOutline = $series->getOutline(); + if ($oOutline instanceof Outline) { + $outlineWidth = $oOutline->getWidth(); + if (!empty($outlineWidth)) { + $outlineWidth = number_format(CommonDrawing::pointsToCentimeters($outlineWidth), 3, '.', ''); + } + $outlineColor = $oOutline->getFill()->getStartColor()->getRGB(); + } + if (empty($outlineWidth)) { + $outlineWidth = '0.079'; + } + if (empty($outlineColor)) { + $outlineColor = '4a7ebb'; + } + $this->xmlContent->writeAttribute('svg:stroke-width', $outlineWidth.'cm'); + $this->xmlContent->writeAttribute('svg:stroke-color', '#'.$outlineColor); } else { $this->xmlContent->writeAttribute('draw:stroke', 'none'); if (!($chartType instanceof Area)) { @@ -645,10 +727,10 @@ private function writeSeriesStyle(Chart $chart, Chart\Series $series) $this->xmlContent->writeAttribute('fo:font-size', $series->getFont()->getSize().'pt'); // > style:text-properties $this->xmlContent->endElement(); - + // > style:style $this->xmlContent->endElement(); - + foreach ($series->getDataPointFills() as $idx => $oFill) { // style:style $this->xmlContent->startElement('style:style'); @@ -664,7 +746,7 @@ private function writeSeriesStyle(Chart $chart, Chart\Series $series) $this->xmlContent->endElement(); } } - + /** */ private function writeTable() @@ -672,7 +754,7 @@ private function writeTable() // table:table $this->xmlContent->startElement('table:table'); $this->xmlContent->writeAttribute('table:name', 'table-local'); - + // table:table-header-columns $this->xmlContent->startElement('table:table-header-columns'); // table:table-column @@ -681,7 +763,7 @@ private function writeTable() $this->xmlContent->endElement(); // > table:table-header-columns $this->xmlContent->endElement(); - + // table:table-columns $this->xmlContent->startElement('table:table-columns'); // table:table-column @@ -694,7 +776,7 @@ private function writeTable() $this->xmlContent->endElement(); // > table:table-columns $this->xmlContent->endElement(); - + // table:table-header-rows $this->xmlContent->startElement('table:table-header-rows'); // table:table-row @@ -722,22 +804,21 @@ private function writeTable() $this->xmlContent->endElement(); // > table:table-header-rows $this->xmlContent->endElement(); - + // table:table-rows $this->xmlContent->startElement('table:table-rows'); - + foreach ($this->arrayData as $row) { // table:table-row $this->xmlContent->startElement('table:table-row'); foreach ($row as $cell) { // table:table-cell $this->xmlContent->startElement('table:table-cell'); - if (is_numeric($cell)) { - $this->xmlContent->writeAttribute('office:value-type', 'float'); - $this->xmlContent->writeAttribute('office:value', $cell); - } else { - $this->xmlContent->writeAttribute('office:value-type', 'string'); - } + + $cellNumeric = is_numeric($cell); + $this->xmlContent->writeAttributeIf(!$cellNumeric, 'office:value-type', 'string'); + $this->xmlContent->writeAttributeIf($cellNumeric, 'office:value-type', 'float'); + $this->xmlContent->writeAttributeIf($cellNumeric, 'office:value', $cell); // text:p $this->xmlContent->startElement('text:p'); $this->xmlContent->text($cell); @@ -749,57 +830,59 @@ private function writeTable() // > table:table-row $this->xmlContent->endElement(); } - + // > table:table-rows $this->xmlContent->endElement(); // > table:table $this->xmlContent->endElement(); } - + /** * @param Title $oTitle */ private function writeTitle(Title $oTitle) { - if ($oTitle->isVisible()) { - // chart:title - $this->xmlContent->startElement('chart:title'); - $this->xmlContent->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters($oTitle->getOffsetX()), 3) . 'cm'); - $this->xmlContent->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters($oTitle->getOffsetY()), 3) . 'cm'); - $this->xmlContent->writeAttribute('chart:style-name', 'styleTitle'); - // > text:p - $this->xmlContent->startElement('text:p'); - $this->xmlContent->text($oTitle->getText()); - // > text:p - $this->xmlContent->endElement(); - // > chart:title - $this->xmlContent->endElement(); + if (!$oTitle->isVisible()) { + return; } + // chart:title + $this->xmlContent->startElement('chart:title'); + $this->xmlContent->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters($oTitle->getOffsetX()), 3) . 'cm'); + $this->xmlContent->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters($oTitle->getOffsetY()), 3) . 'cm'); + $this->xmlContent->writeAttribute('chart:style-name', 'styleTitle'); + // > text:p + $this->xmlContent->startElement('text:p'); + $this->xmlContent->text($oTitle->getText()); + // > text:p + $this->xmlContent->endElement(); + // > chart:title + $this->xmlContent->endElement(); } - + /** * @param Title $oTitle */ private function writeTitleStyle(Title $oTitle) { - if ($oTitle->isVisible()) { - // style:style - $this->xmlContent->startElement('style:style'); - $this->xmlContent->writeAttribute('style:name', 'styleTitle'); - $this->xmlContent->writeAttribute('style:family', 'chart'); - // style:text-properties - $this->xmlContent->startElement('style:text-properties'); - $this->xmlContent->writeAttribute('fo:color', '#'.$oTitle->getFont()->getColor()->getRGB()); - $this->xmlContent->writeAttribute('fo:font-family', $oTitle->getFont()->getName()); - $this->xmlContent->writeAttribute('fo:font-size', $oTitle->getFont()->getSize().'pt'); - $this->xmlContent->writeAttribute('fo:font-style', $oTitle->getFont()->isItalic() ? 'italic' : 'normal'); - // > style:text-properties - $this->xmlContent->endElement(); - // > style:style - $this->xmlContent->endElement(); + if (!$oTitle->isVisible()) { + return; } + // style:style + $this->xmlContent->startElement('style:style'); + $this->xmlContent->writeAttribute('style:name', 'styleTitle'); + $this->xmlContent->writeAttribute('style:family', 'chart'); + // style:text-properties + $this->xmlContent->startElement('style:text-properties'); + $this->xmlContent->writeAttribute('fo:color', '#'.$oTitle->getFont()->getColor()->getRGB()); + $this->xmlContent->writeAttribute('fo:font-family', $oTitle->getFont()->getName()); + $this->xmlContent->writeAttribute('fo:font-size', $oTitle->getFont()->getSize().'pt'); + $this->xmlContent->writeAttribute('fo:font-style', $oTitle->getFont()->isItalic() ? 'italic' : 'normal'); + // > style:text-properties + $this->xmlContent->endElement(); + // > style:style + $this->xmlContent->endElement(); } - + private function writeWall() { // chart:wall @@ -815,7 +898,7 @@ private function writeWall() private function writeWallStyle(Chart $chart) { $chartType = $chart->getPlotArea()->getType(); - + // style:style $this->xmlContent->startElement('style:style'); $this->xmlContent->writeAttribute('style:name', 'styleWall'); diff --git a/src/PhpPresentation/Writer/ODPresentation/Pictures.php b/src/PhpPresentation/Writer/ODPresentation/Pictures.php new file mode 100644 index 000000000..32ce96819 --- /dev/null +++ b/src/PhpPresentation/Writer/ODPresentation/Pictures.php @@ -0,0 +1,37 @@ +getDrawingHashTable()->count(); ++$i) { + $shape = $this->getDrawingHashTable()->getByIndex($i); + if (!($shape instanceof Drawing\AbstractDrawingAdapter)) { + continue; + } + $arrMedia[] = $shape->getIndexedFilename(); + $this->getZip()->addFromString('Pictures/' . $shape->getIndexedFilename(), $shape->getContents()); + } + + foreach ($this->getPresentation()->getAllSlides() as $keySlide => $oSlide) { + // Add background image slide + $oBkgImage = $oSlide->getBackground(); + if ($oBkgImage instanceof Image) { + $this->getZip()->addFromString('Pictures/'.$oBkgImage->getIndexedFilename($keySlide), file_get_contents($oBkgImage->getPath())); + } + } + + return $this->getZip(); + } +} diff --git a/src/PhpPresentation/Writer/ODPresentation/Styles.php b/src/PhpPresentation/Writer/ODPresentation/Styles.php index 7f4ca5a4f..7cbd0d4cd 100644 --- a/src/PhpPresentation/Writer/ODPresentation/Styles.php +++ b/src/PhpPresentation/Writer/ODPresentation/Styles.php @@ -1,26 +1,10 @@ getZip()->addFromString('styles.xml', $this->writePart()); + return $this->getZip(); + } + /** * Write Meta file to XML format * - * @param PhpPresentation $pPhpPresentation * @return string XML Output * @throws \Exception */ - public function writePart(PhpPresentation $pPhpPresentation) + protected function writePart() { // Create XML writer - $objWriter = $this->getXMLWriter(); - - // XML header + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); $objWriter->startDocument('1.0', 'UTF-8'); // office:document-meta @@ -97,7 +84,10 @@ public function writePart(PhpPresentation $pPhpPresentation) $objWriter->writeAttribute('office:version', '1.2'); // Variables - $stylePageLayout = $pPhpPresentation->getLayout()->getDocumentLayout(); + $stylePageLayout = $this->getPresentation()->getLayout()->getDocumentLayout(); + if (empty($stylePageLayout)) { + $stylePageLayout = 'sPL0'; + } // office:styles $objWriter->startElement('office:styles'); @@ -114,7 +104,7 @@ public function writePart(PhpPresentation $pPhpPresentation) // > style:style $objWriter->endElement(); - foreach ($pPhpPresentation->getAllSlides() as $keySlide => $oSlide) { + foreach ($this->getPresentation()->getAllSlides() as $keySlide => $oSlide) { foreach ($oSlide->getShapeCollection() as $shape) { if ($shape instanceof Table) { $this->writeTableStyle($objWriter, $shape); @@ -136,24 +126,20 @@ public function writePart(PhpPresentation $pPhpPresentation) $objWriter->startElement('office:automatic-styles'); // style:page-layout $objWriter->startElement('style:page-layout'); - if (empty($stylePageLayout)) { - $objWriter->writeAttribute('style:name', 'sPL0'); - } else { - $objWriter->writeAttribute('style:name', $stylePageLayout); - } + $objWriter->writeAttribute('style:name', $stylePageLayout); // style:page-layout-properties $objWriter->startElement('style:page-layout-properties'); $objWriter->writeAttribute('fo:margin-top', '0cm'); $objWriter->writeAttribute('fo:margin-bottom', '0cm'); $objWriter->writeAttribute('fo:margin-left', '0cm'); $objWriter->writeAttribute('fo:margin-right', '0cm'); - $objWriter->writeAttribute('fo:page-width', Text::numberFormat(CommonDrawing::pixelsToCentimeters(CommonDrawing::emuToPixels($pPhpPresentation->getLayout()->getCX())), 1) . 'cm'); - $objWriter->writeAttribute('fo:page-height', Text::numberFormat(CommonDrawing::pixelsToCentimeters(CommonDrawing::emuToPixels($pPhpPresentation->getLayout()->getCY())), 1) . 'cm'); - if ($pPhpPresentation->getLayout()->getCX() > $pPhpPresentation->getLayout()->getCY()) { - $objWriter->writeAttribute('style:print-orientation', 'landscape'); - } else { - $objWriter->writeAttribute('style:print-orientation', 'portrait'); + $objWriter->writeAttribute('fo:page-width', Text::numberFormat(CommonDrawing::pixelsToCentimeters(CommonDrawing::emuToPixels($this->getPresentation()->getLayout()->getCX())), 1) . 'cm'); + $objWriter->writeAttribute('fo:page-height', Text::numberFormat(CommonDrawing::pixelsToCentimeters(CommonDrawing::emuToPixels($this->getPresentation()->getLayout()->getCY())), 1) . 'cm'); + $printOrientation = 'portrait'; + if ($this->getPresentation()->getLayout()->getCX() > $this->getPresentation()->getLayout()->getCY()) { + $printOrientation = 'landscape'; } + $objWriter->writeAttribute('style:print-orientation', $printOrientation); $objWriter->endElement(); $objWriter->endElement(); $objWriter->endElement(); @@ -164,40 +150,38 @@ public function writePart(PhpPresentation $pPhpPresentation) $objWriter->startElement('style:master-page'); $objWriter->writeAttribute('style:name', 'Standard'); $objWriter->writeAttribute('style:display-name', 'Standard'); - if (empty($stylePageLayout)) { - $objWriter->writeAttribute('style:page-layout-name', 'sPL0'); - } else { - $objWriter->writeAttribute('style:page-layout-name', $stylePageLayout); - } + $objWriter->writeAttribute('style:page-layout-name', $stylePageLayout); $objWriter->writeAttribute('draw:style-name', 'sPres0'); $objWriter->endElement(); $objWriter->endElement(); - + $objWriter->endElement(); // Return return $objWriter->getData(); } - + /** * Write the default style information for a RichText shape * * @param XMLWriter $objWriter * @param RichText $shape */ - public function writeRichTextStyle(XMLWriter $objWriter, RichText $shape) + protected function writeRichTextStyle(XMLWriter $objWriter, RichText $shape) { - if ($shape->getFill()->getFillType() == Fill::FILL_GRADIENT_LINEAR || $shape->getFill()->getFillType() == Fill::FILL_GRADIENT_PATH) { - if (!in_array($shape->getFill()->getHashCode(), $this->arrayGradient)) { - $this->writeGradientFill($objWriter, $shape->getFill()); + $oFill = $shape->getFill(); + if ($oFill->getFillType() == Fill::FILL_GRADIENT_LINEAR || $oFill->getFillType() == Fill::FILL_GRADIENT_PATH) { + if (!in_array($oFill->getHashCode(), $this->arrayGradient)) { + $this->writeGradientFill($objWriter, $oFill); } } - if ($shape->getBorder()->getDashStyle() != Border::DASH_SOLID) { - if (!in_array($shape->getBorder()->getDashStyle(), $this->arrayStrokeDash)) { + $oBorder = $shape->getBorder(); + if ($oBorder->getDashStyle() != Border::DASH_SOLID) { + if (!in_array($oBorder->getDashStyle(), $this->arrayStrokeDash)) { $objWriter->startElement('draw:stroke-dash'); - $objWriter->writeAttribute('draw:name', 'strokeDash_'.$shape->getBorder()->getDashStyle()); + $objWriter->writeAttribute('draw:name', 'strokeDash_'.$oBorder->getDashStyle()); $objWriter->writeAttribute('draw:style', 'rect'); - switch ($shape->getBorder()->getDashStyle()) { + switch ($oBorder->getDashStyle()) { case Border::DASH_DASH: $objWriter->writeAttribute('draw:distance', '0.105cm'); $objWriter->writeAttribute('draw:dots2', '1'); @@ -260,18 +244,18 @@ public function writeRichTextStyle(XMLWriter $objWriter, RichText $shape) break; } $objWriter->endElement(); - $this->arrayStrokeDash[] = $shape->getBorder()->getDashStyle(); + $this->arrayStrokeDash[] = $oBorder->getDashStyle(); } } } - + /** * Write the default style information for a Table shape * * @param XMLWriter $objWriter * @param Table $shape */ - public function writeTableStyle(XMLWriter $objWriter, Table $shape) + protected function writeTableStyle(XMLWriter $objWriter, Table $shape) { foreach ($shape->getRows() as $row) { foreach ($row->getCells() as $cell) { @@ -290,7 +274,7 @@ public function writeTableStyle(XMLWriter $objWriter, Table $shape) * @param XMLWriter $objWriter * @param Group $group */ - public function writeGroupStyle(XMLWriter $objWriter, Group $group) + protected function writeGroupStyle(XMLWriter $objWriter, Group $group) { $shapes = $group->getShapeCollection(); foreach ($shapes as $shape) { @@ -301,7 +285,7 @@ public function writeGroupStyle(XMLWriter $objWriter, Group $group) } } } - + /** * Write the gradient style * @param XMLWriter $objWriter diff --git a/src/PhpPresentation/Writer/ODPresentation/ThumbnailsThumbnail.php b/src/PhpPresentation/Writer/ODPresentation/ThumbnailsThumbnail.php new file mode 100644 index 000000000..c211761d7 --- /dev/null +++ b/src/PhpPresentation/Writer/ODPresentation/ThumbnailsThumbnail.php @@ -0,0 +1,41 @@ +getPresentation()->getPresentationProperties()->getThumbnailPath(); + if ($pathThumbnail) { + // Size : 128x128 pixel + // PNG : 8bit, non-interlaced with full alpha transparency + $gdImage = imagecreatefromstring(file_get_contents($pathThumbnail)); + if ($gdImage) { + list($width, $height) = getimagesize($pathThumbnail); + + $gdRender = imagecreatetruecolor(128, 128); + $colorBgAlpha = imagecolorallocatealpha($gdRender, 0, 0, 0, 127); + imagecolortransparent($gdRender, $colorBgAlpha); + imagefill($gdRender, 0, 0, $colorBgAlpha); + imagecopyresampled($gdRender, $gdImage, 0, 0, 0, 0, 128, 128, $width, $height); + imagetruecolortopalette($gdRender, false, 255); + imagesavealpha($gdRender, true); + + ob_start(); + imagepng($gdRender); + $imageContents = ob_get_contents(); + ob_end_clean(); + + imagedestroy($gdRender); + imagedestroy($gdImage); + + $this->getZip()->addFromString('Thumbnails/thumbnail.png', $imageContents); + } + } + return $this->getZip(); + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007.php b/src/PhpPresentation/Writer/PowerPoint2007.php index 6ada7f59a..a6814a21b 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007.php +++ b/src/PhpPresentation/Writer/PowerPoint2007.php @@ -17,57 +17,21 @@ namespace PhpOffice\PhpPresentation\Writer; +use DirectoryIterator; +use PhpOffice\Common\Adapter\Zip\ZipArchiveAdapter; use PhpOffice\PhpPresentation\HashTable; use PhpOffice\PhpPresentation\PhpPresentation; -use PhpOffice\PhpPresentation\Shape\Drawing as DrawingShape; -use PhpOffice\PhpPresentation\Shape\MemoryDrawing as MemoryDrawingShape; +use PhpOffice\PhpPresentation\Shape\AbstractDrawing; use PhpOffice\PhpPresentation\Shape\Chart as ChartShape; -use PhpOffice\PhpPresentation\Slide\Background\Image; -use PhpOffice\PhpPresentation\Writer\PowerPoint2007\AbstractLayoutPack; -use PhpOffice\PhpPresentation\Writer\PowerPoint2007\Chart; -use PhpOffice\PhpPresentation\Writer\PowerPoint2007\ContentTypes; -use PhpOffice\PhpPresentation\Writer\PowerPoint2007\DocProps; -use PhpOffice\PhpPresentation\Writer\PowerPoint2007\Drawing; +use PhpOffice\PhpPresentation\Shape\Table; +use PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack\AbstractLayoutPack; use PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack\PackDefault; -use PhpOffice\PhpPresentation\Writer\PowerPoint2007\PptProps; -use PhpOffice\PhpPresentation\Writer\PowerPoint2007\Presentation; -use PhpOffice\PhpPresentation\Writer\PowerPoint2007\Rels; -use PhpOffice\PhpPresentation\Writer\PowerPoint2007\Slide; -use PhpOffice\PhpPresentation\Writer\PowerPoint2007\Theme; /** * \PhpOffice\PhpPresentation\Writer\PowerPoint2007 */ -class PowerPoint2007 implements WriterInterface +class PowerPoint2007 extends AbstractWriter implements WriterInterface { - /** - * Office2003 compatibility - * - * @var boolean - */ - protected $office2003comp = false; - - /** - * Private writer parts - * - * @var \PhpOffice\PhpPresentation\Writer\PowerPoint2007\AbstractPart[] - */ - protected $writerParts; - - /** - * Private PhpPresentation - * - * @var \PhpOffice\PhpPresentation\PhpPresentation - */ - protected $presentation; - - /** - * Private unique hash table - * - * @var \PhpOffice\PhpPresentation\HashTable - */ - protected $drawingHashTable; - /** * Use disk caching where possible? * @@ -84,13 +48,13 @@ class PowerPoint2007 implements WriterInterface /** * Layout pack to use - * - * @var \PhpOffice\PhpPresentation\Writer\PowerPoint2007\AbstractLayoutPack + * @deprecated 0.7 + * @var \PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack\AbstractLayoutPack */ protected $layoutPack; /** - * Create a new \PhpOffice\PhpPresentation\Writer\PowerPoint2007 + * Create a new PowerPoint2007 file * * @param PhpPresentation $pPhpPresentation */ @@ -105,39 +69,10 @@ public function __construct(PhpPresentation $pPhpPresentation = null) // Set layout pack $this->layoutPack = new PackDefault(); - // Initialise writer parts - $this->writerParts['contenttypes'] = new ContentTypes(); - $this->writerParts['pptprops'] = new PptProps(); - $this->writerParts['docprops'] = new DocProps(); - $this->writerParts['rels'] = new Rels(); - $this->writerParts['theme'] = new Theme(); - $this->writerParts['presentation'] = new Presentation(); - $this->writerParts['slide'] = new Slide(); - $this->writerParts['drawing'] = new Drawing(); - $this->writerParts['chart'] = new Chart(); - - // Assign parent WriterInterface - foreach ($this->writerParts as $writer) { - $writer->setParentWriter($this); - } - // Set HashTable variables - $this->drawingHashTable = new HashTable(); - } + $this->oDrawingHashTable = new HashTable(); - /** - * Get writer part - * - * @param string $pPartName Writer part name - * @return \PhpOffice\PhpPresentation\Writer\PowerPoint2007\AbstractPart - */ - public function getWriterPart($pPartName = '') - { - if ($pPartName != '' && isset($this->writerParts[strtolower($pPartName)])) { - return $this->writerParts[strtolower($pPartName)]; - } else { - return null; - } + $this->setZipAdapter(new ZipArchiveAdapter()); } /** @@ -151,259 +86,63 @@ public function save($pFilename) if (empty($pFilename)) { throw new \Exception("Filename is empty"); } - if (!is_null($this->presentation)) { - // If $pFilename is php://output or php://stdout, make it a temporary file... - $originalFilename = $pFilename; - if (strtolower($pFilename) == 'php://output' || strtolower($pFilename) == 'php://stdout') { - $pFilename = @tempnam('./', 'phppttmp'); - if ($pFilename == '') { - $pFilename = $originalFilename; - } - } - - $wPartDrawing = $this->getWriterPart('Drawing'); - if (!$wPartDrawing instanceof Drawing) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\PowerPoint2007\Drawing'); - } - $wPartContentTypes = $this->getWriterPart('ContentTypes'); - if (!$wPartContentTypes instanceof ContentTypes) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\PowerPoint2007\ContentTypes'); - } - $wPartRels = $this->getWriterPart('Rels'); - if (!$wPartRels instanceof Rels) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\PowerPoint2007\Rels'); - } - $wPartDocProps = $this->getWriterPart('DocProps'); - if (!$wPartDocProps instanceof DocProps) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\PowerPoint2007\DocProps'); - } - $wPartTheme = $this->getWriterPart('Theme'); - if (!$wPartTheme instanceof Theme) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\PowerPoint2007\Theme'); - } - $wPartPresentation = $this->getWriterPart('Presentation'); - if (!$wPartPresentation instanceof Presentation) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\PowerPoint2007\Presentation'); - } - $wPartSlide = $this->getWriterPart('Slide'); - if (!$wPartSlide instanceof Slide) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\PowerPoint2007\Slide'); - } - $wPartChart = $this->getWriterPart('Chart'); - if (!$wPartChart instanceof Chart) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\PowerPoint2007\Chart'); - } - $wPptProps = $this->getWriterPart('PptProps'); - if (!$wPptProps instanceof PptProps) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\PowerPoint2007\PptProps'); - } - - // Create drawing dictionary - $this->drawingHashTable->addFromSource($wPartDrawing->allDrawings($this->presentation)); - - // Create new ZIP file and open it for writing - $objZip = new \ZipArchive(); + $oPresentation = $this->getPhpPresentation(); - // Try opening the ZIP file - if ($objZip->open($pFilename, \ZipArchive::OVERWRITE) !== true) { - if ($objZip->open($pFilename, \ZipArchive::CREATE) !== true) { - throw new \Exception("Could not open " . $pFilename . " for writing."); - } + // If $pFilename is php://output or php://stdout, make it a temporary file... + $originalFilename = $pFilename; + if (strtolower($pFilename) == 'php://output' || strtolower($pFilename) == 'php://stdout') { + $pFilename = @tempnam('./', 'phppttmp'); + if ($pFilename == '') { + $pFilename = $originalFilename; } + } - // Add [Content_Types].xml to ZIP file - $objZip->addFromString('[Content_Types].xml', $wPartContentTypes->writeContentTypes($this->presentation)); - - // Add PPT properties and styles to ZIP file - Required for Apple Keynote compatibility. - $objZip->addFromString('ppt/presProps.xml', $wPptProps->writePresProps($this->presentation)); - $objZip->addFromString('ppt/tableStyles.xml', $wPptProps->writeTableStyles()); - $objZip->addFromString('ppt/viewProps.xml', $wPptProps->writeViewProps($this->presentation)); - - // Add relationships to ZIP file - $objZip->addFromString('_rels/.rels', $wPartRels->writeRelationships()); - $objZip->addFromString('ppt/_rels/presentation.xml.rels', $wPartRels->writePresentationRelationships($this->presentation)); - - // Add document properties to ZIP file - $objZip->addFromString('docProps/app.xml', $wPartDocProps->writeDocPropsApp($this->presentation)); - $objZip->addFromString('docProps/core.xml', $wPartDocProps->writeDocPropsCore($this->presentation)); - $objZip->addFromString('docProps/custom.xml', $wPartDocProps->writeDocPropsCustom($this->presentation)); - - $masterSlides = $this->getLayoutPack()->getMasterSlides(); - foreach ($masterSlides as $masterSlide) { - // Add themes to ZIP file - $objZip->addFromString('ppt/theme/_rels/theme' . $masterSlide['masterid'] . '.xml.rels', $wPartRels->writeThemeRelationships($masterSlide['masterid'])); - $objZip->addFromString('ppt/theme/theme' . $masterSlide['masterid'] . '.xml', $wPartTheme->writeTheme($masterSlide['masterid'])); - // Add slide masters to ZIP file - $objZip->addFromString('ppt/slideMasters/_rels/slideMaster' . $masterSlide['masterid'] . '.xml.rels', $wPartRels->writeSlideMasterRelationships($masterSlide['masterid'])); - $objZip->addFromString('ppt/slideMasters/slideMaster' . $masterSlide['masterid'] . '.xml', $masterSlide['body']); - } + // Create drawing dictionary + $this->getDrawingHashTable()->addFromSource($this->allDrawings()); - // Add slide layouts to ZIP file - $slideLayouts = $this->getLayoutPack()->getLayouts(); - foreach ($slideLayouts as $key => $layout) { - $objZip->addFromString('ppt/slideLayouts/_rels/slideLayout' . $key . '.xml.rels', $wPartRels->writeSlideLayoutRelationships($key, $layout['masterid'])); - $objZip->addFromString('ppt/slideLayouts/slideLayout' . $key . '.xml', utf8_encode($layout['body'])); - } + $oZip = $this->getZipAdapter(); + $oZip->open($pFilename); - // Add layoutpack relations - $otherRelations = $this->getLayoutPack()->getMasterSlideRelations(); - foreach ($otherRelations as $otherRelation) { - if (strpos($otherRelation['target'], 'http://') !== 0) { - $objZip->addFromString($this->absoluteZipPath('ppt/slideMasters/' . $otherRelation['target']), $otherRelation['contents']); - } - } - $otherRelations = $this->getLayoutPack()->getThemeRelations(); - foreach ($otherRelations as $otherRelation) { - if (strpos($otherRelation['target'], 'http://') !== 0) { - $objZip->addFromString($this->absoluteZipPath('ppt/theme/' . $otherRelation['target']), $otherRelation['contents']); - } - } - $otherRelations = $this->getLayoutPack()->getLayoutRelations(); - foreach ($otherRelations as $otherRelation) { - if (strpos($otherRelation['target'], 'http://') !== 0) { - $objZip->addFromString($this->absoluteZipPath('ppt/slideLayouts/' . $otherRelation['target']), $otherRelation['contents']); - } + $oDir = new DirectoryIterator(dirname(__FILE__).DIRECTORY_SEPARATOR.'PowerPoint2007'); + foreach ($oDir as $oFile) { + if (!$oFile->isFile()) { + continue; } - // Add presentation to ZIP file - $objZip->addFromString('ppt/presentation.xml', $wPartPresentation->writePresentation($this->presentation)); + $class = __NAMESPACE__ . '\\PowerPoint2007\\' . $oFile->getBasename('.php'); + $o = new \ReflectionClass($class); - // Add slides (drawings, ...) and slide relationships (drawings, ...) - for ($i = 0; $i < $this->presentation->getSlideCount(); ++$i) { - $oSlide = $this->presentation->getSlide($i); - // Add slide - $objZip->addFromString('ppt/slides/_rels/slide' . ($i + 1) . '.xml.rels', $wPartRels->writeSlideRelationships($oSlide)); - $objZip->addFromString('ppt/slides/slide' . ($i + 1) . '.xml', $wPartSlide->writeSlide($oSlide)); - // Add note slide - if ($oSlide->getNote()->getShapeCollection()->count() > 0) { - $objZip->addFromString('ppt/notesSlides/notesSlide' . ($i + 1) . '.xml', $wPartSlide->writeNote($oSlide->getNote())); - } - // Add background image slide - $oBkgImage = $oSlide->getBackground(); - if ($oBkgImage instanceof Image) { - $objZip->addFromString('ppt/media/'.$oBkgImage->getIndexedFilename($i), file_get_contents($oBkgImage->getPath())); - } + if ($o->isAbstract() || !$o->isSubclassOf('PhpOffice\PhpPresentation\Writer\PowerPoint2007\AbstractDecoratorWriter')) { + continue; } + $arrayFiles[$oFile->getBasename('.php')] = $o; + } - // Add media - for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) { - $shape = $this->getDrawingHashTable()->getByIndex($i); - if ($shape instanceof DrawingShape) { - $imagePath = $shape->getPath(); - - if (strpos($imagePath, 'zip://') !== false) { - $imagePath = substr($imagePath, 6); - $imagePathSplitted = explode('#', $imagePath); - - $imageZip = new \ZipArchive(); - $imageZip->open($imagePathSplitted[0]); - $imageContents = $imageZip->getFromName($imagePathSplitted[1]); - $imageZip->close(); - unset($imageZip); - } else { - $imageContents = file_get_contents($imagePath); - } - - $objZip->addFromString('ppt/media/' . str_replace(' ', '_', $shape->getIndexedFilename()), $imageContents); - } elseif ($shape instanceof MemoryDrawingShape) { - ob_start(); - call_user_func($shape->getRenderingFunction(), $shape->getImageResource()); - $imageContents = ob_get_contents(); - ob_end_clean(); + ksort($arrayFiles); - $objZip->addFromString('ppt/media/' . str_replace(' ', '_', $shape->getIndexedFilename()), $imageContents); - } elseif ($shape instanceof ChartShape) { - $objZip->addFromString('ppt/charts/' . $shape->getIndexedFilename(), $wPartChart->writeChart($shape)); + foreach ($arrayFiles as $o) { + $oService = $o->newInstance(); + $oService->setZip($oZip); + $oService->setPresentation($oPresentation); + $oService->setDrawingHashTable($this->getDrawingHashTable()); + $oZip = $oService->render(); + unset($oService); + } - // Chart relations? - if ($shape->hasIncludedSpreadsheet()) { - $objZip->addFromString('ppt/charts/_rels/' . $shape->getIndexedFilename() . '.rels', $wPartRels->writeChartRelationships($shape)); - $objZip->addFromString('ppt/embeddings/' . $shape->getIndexedFilename() . '.xlsx', $wPartChart->writeSpreadsheet($this->presentation, $shape, $pFilename . '.xlsx')); - } - } - } + // Close file + $oZip->close(); - // Close file - if ($objZip->close() === false) { - throw new \Exception("Could not close zip file $pFilename."); + // If a temporary file was used, copy it to the correct file stream + if ($originalFilename != $pFilename) { + if (copy($pFilename, $originalFilename) === false) { + throw new \Exception("Could not copy temporary zip file $pFilename to $originalFilename."); } - - // If a temporary file was used, copy it to the correct file stream - if ($originalFilename != $pFilename) { - if (copy($pFilename, $originalFilename) === false) { - throw new \Exception("Could not copy temporary zip file $pFilename to $originalFilename."); - } - if (@unlink($pFilename) === false) { - throw new \Exception('The file '.$pFilename.' could not be removed.'); - } + if (@unlink($pFilename) === false) { + throw new \Exception('The file '.$pFilename.' could not be removed.'); } - } else { - throw new \Exception("PhpPresentation object unassigned."); - } - } - - /** - * Get PhpPresentation object - * - * @return PhpPresentation - * @throws \Exception - */ - public function getPhpPresentation() - { - if (!is_null($this->presentation)) { - return $this->presentation; - } else { - throw new \Exception("No PhpPresentation assigned."); } } - /** - * Get PhpPresentation object - * - * @param PhpPresentation $pPhpPresentation PhpPresentation object - * @throws \Exception - * @return \PhpOffice\PhpPresentation\Writer\PowerPoint2007 - */ - public function setPhpPresentation(PhpPresentation $pPhpPresentation = null) - { - $this->presentation = $pPhpPresentation; - - return $this; - } - - /** - * Get hash table - * - * @return \PhpOffice\PhpPresentation\HashTable - */ - public function getDrawingHashTable() - { - return $this->drawingHashTable; - } - - /** - * Get Office2003 compatibility - * - * @return boolean - */ - public function hasOffice2003Compatibility() - { - return $this->office2003comp; - } - - /** - * Set Pre-Calculate Formulas - * - * @param boolean $pValue Office2003 compatibility? - * @return \PhpOffice\PhpPresentation\Writer\PowerPoint2007 - */ - public function setOffice2003Compatibility($pValue = false) - { - $this->office2003comp = $pValue; - - return $this; - } - /** * Get use disk caching where possible? * @@ -427,11 +166,10 @@ public function setUseDiskCaching($pValue = false, $pDirectory = null) $this->useDiskCaching = $pValue; if (!is_null($pDirectory)) { - if (is_dir($pDirectory)) { - $this->diskCachingDir = $pDirectory; - } else { + if (!is_dir($pDirectory)) { throw new \Exception("Directory does not exist: $pDirectory"); } + $this->diskCachingDir = $pDirectory; } return $this; @@ -450,7 +188,8 @@ public function getDiskCachingDirectory() /** * Get layout pack to use * - * @return \PhpOffice\PhpPresentation\Writer\PowerPoint2007\AbstractLayoutPack + * @deprecated 0.7 + * @return \PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack\AbstractLayoutPack */ public function getLayoutPack() { @@ -460,7 +199,8 @@ public function getLayoutPack() /** * Set layout pack to use * - * @param \PhpOffice\PhpPresentation\Writer\PowerPoint2007\AbstractLayoutPack $pValue + * @deprecated 0.7 + * @param \PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack\AbstractLayoutPack $pValue * @return \PhpOffice\PhpPresentation\Writer\PowerPoint2007 */ public function setLayoutPack(AbstractLayoutPack $pValue = null) @@ -469,32 +209,4 @@ public function setLayoutPack(AbstractLayoutPack $pValue = null) return $this; } - - /** - * Determine absolute zip path - * - * @param string $path - * @return string - */ - protected function absoluteZipPath($path) - { - $path = str_replace(array( - '/', - '\\' - ), DIRECTORY_SEPARATOR, $path); - $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen'); - $absolutes = array(); - foreach ($parts as $part) { - if ('.' == $part) { - continue; - } - if ('..' == $part) { - array_pop($absolutes); - } else { - $absolutes[] = $part; - } - } - - return implode('/', $absolutes); - } } diff --git a/src/PhpPresentation/Writer/PowerPoint2007/AbstractDecoratorWriter.php b/src/PhpPresentation/Writer/PowerPoint2007/AbstractDecoratorWriter.php new file mode 100644 index 000000000..6338b2a94 --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/AbstractDecoratorWriter.php @@ -0,0 +1,309 @@ +startElement('Relationship'); + $objWriter->writeAttribute('Id', $pId); + $objWriter->writeAttribute('Type', $pType); + $objWriter->writeAttribute('Target', $pTarget); + + if ($pTargetMode != '') { + $objWriter->writeAttribute('TargetMode', $pTargetMode); + } + + $objWriter->endElement(); + } + + /** + * Write Border + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Style\Border $pBorder Border + * @param string $pElementName Element name + * @throws \Exception + */ + protected function writeBorder(XMLWriter $objWriter, $pBorder, $pElementName = 'L') + { + if (!($pBorder instanceof Border)) { + return; + } + + if ($pBorder->getLineStyle() == Border::LINE_NONE && $pElementName == '') { + return; + } + + // Line style + $lineStyle = $pBorder->getLineStyle(); + if ($lineStyle == Border::LINE_NONE) { + $lineStyle = Border::LINE_SINGLE; + } + + // Line width + $lineWidth = 12700 * $pBorder->getLineWidth(); + + // a:ln $pElementName + $objWriter->startElement('a:ln' . $pElementName); + $objWriter->writeAttribute('w', $lineWidth); + $objWriter->writeAttribute('cap', 'flat'); + $objWriter->writeAttribute('cmpd', $lineStyle); + $objWriter->writeAttribute('algn', 'ctr'); + + // Fill? + if ($pBorder->getLineStyle() == Border::LINE_NONE) { + // a:noFill + $objWriter->writeElement('a:noFill', null); + } else { + // a:solidFill + $objWriter->startElement('a:solidFill'); + $this->writeColor($objWriter, $pBorder->getColor()); + $objWriter->endElement(); + } + + // Dash + $objWriter->startElement('a:prstDash'); + $objWriter->writeAttribute('val', $pBorder->getDashStyle()); + $objWriter->endElement(); + + // a:round + $objWriter->writeElement('a:round', null); + + // a:headEnd + $objWriter->startElement('a:headEnd'); + $objWriter->writeAttribute('type', 'none'); + $objWriter->writeAttribute('w', 'med'); + $objWriter->writeAttribute('len', 'med'); + $objWriter->endElement(); + + // a:tailEnd + $objWriter->startElement('a:tailEnd'); + $objWriter->writeAttribute('type', 'none'); + $objWriter->writeAttribute('w', 'med'); + $objWriter->writeAttribute('len', 'med'); + $objWriter->endElement(); + + $objWriter->endElement(); + } + + /** + * @param XMLWriter $objWriter + * @param Color $color + * @param int|null $alpha + */ + protected function writeColor(XMLWriter $objWriter, Color $color, $alpha = null) + { + if (is_null($alpha)) { + $alpha = $color->getAlpha(); + } + + // a:srgbClr + $objWriter->startElement('a:srgbClr'); + $objWriter->writeAttribute('val', $color->getRGB()); + + // a:alpha + $objWriter->startElement('a:alpha'); + $objWriter->writeAttribute('val', $alpha * 1000); + $objWriter->endElement(); + + $objWriter->endElement(); + } + + /** + * Write Fill + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Style\Fill $pFill Fill style + * @throws \Exception + */ + protected function writeFill(XMLWriter $objWriter, $pFill) + { + if (! $pFill instanceof Fill) { + return; + } + + // Is it a fill? + if ($pFill->getFillType() == Fill::FILL_NONE) { + return; + } + + // Is it a solid fill? + if ($pFill->getFillType() == Fill::FILL_SOLID) { + $this->writeSolidFill($objWriter, $pFill); + return; + } + + // Check if this is a pattern type or gradient type + if ($pFill->getFillType() == Fill::FILL_GRADIENT_LINEAR || $pFill->getFillType() == Fill::FILL_GRADIENT_PATH) { + // Gradient fill + $this->writeGradientFill($objWriter, $pFill); + } else { + // Pattern fill + $this->writePatternFill($objWriter, $pFill); + } + } + + /** + * Write Solid Fill + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Style\Fill $pFill Fill style + * @throws \Exception + */ + protected function writeSolidFill(XMLWriter $objWriter, Fill $pFill) + { + // a:gradFill + $objWriter->startElement('a:solidFill'); + $this->writeColor($objWriter, $pFill->getStartColor()); + $objWriter->endElement(); + } + + /** + * Write Gradient Fill + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Style\Fill $pFill Fill style + * @throws \Exception + */ + protected function writeGradientFill(XMLWriter $objWriter, Fill $pFill) + { + // a:gradFill + $objWriter->startElement('a:gradFill'); + + // a:gsLst + $objWriter->startElement('a:gsLst'); + // a:gs + $objWriter->startElement('a:gs'); + $objWriter->writeAttribute('pos', '0'); + $this->writeColor($objWriter, $pFill->getStartColor()); + $objWriter->endElement(); + + // a:gs + $objWriter->startElement('a:gs'); + $objWriter->writeAttribute('pos', '100000'); + $this->writeColor($objWriter, $pFill->getEndColor()); + $objWriter->endElement(); + + $objWriter->endElement(); + + // a:lin + $objWriter->startElement('a:lin'); + $objWriter->writeAttribute('ang', CommonDrawing::degreesToAngle($pFill->getRotation())); + $objWriter->writeAttribute('scaled', '0'); + $objWriter->endElement(); + + $objWriter->endElement(); + } + + /** + * Write Pattern Fill + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Style\Fill $pFill Fill style + * @throws \Exception + */ + protected function writePatternFill(XMLWriter $objWriter, Fill $pFill) + { + // a:pattFill + $objWriter->startElement('a:pattFill'); + + // fgClr + $objWriter->startElement('a:fgClr'); + + $this->writeColor($objWriter, $pFill->getStartColor()); + + $objWriter->endElement(); + + // bgClr + $objWriter->startElement('a:bgClr'); + + $this->writeColor($objWriter, $pFill->getEndColor()); + + $objWriter->endElement(); + + $objWriter->endElement(); + } + + /** + * Write Outline + * @param XMLWriter $objWriter + * @param Outline $oOutline + */ + protected function writeOutline(XMLWriter $objWriter, $oOutline) + { + if (!$oOutline instanceof Outline) { + return; + } + // Width : pts + $width = $oOutline->getWidth(); + // Width : pts => px + $width = CommonDrawing::pointsToPixels($width); + // Width : px => emu + $width = CommonDrawing::pixelsToEmu($width); + + // a:ln + $objWriter->startElement('a:ln'); + $objWriter->writeAttribute('w', $width); + + // Fill + $this->writeFill($objWriter, $oOutline->getFill()); + + // > a:ln + $objWriter->endElement(); + } + + /** + * Determine absolute zip path + * + * @param string $path + * @return string + */ + protected function absoluteZipPath($path) + { + $path = str_replace(array( + '/', + '\\' + ), DIRECTORY_SEPARATOR, $path); + $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen'); + $absolutes = array(); + foreach ($parts as $part) { + if ('.' == $part) { + continue; + } + if ('..' == $part) { + array_pop($absolutes); + } else { + $absolutes[] = $part; + } + } + + return implode('/', $absolutes); + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/AbstractPart.php b/src/PhpPresentation/Writer/PowerPoint2007/AbstractPart.php deleted file mode 100644 index 72f65843a..000000000 --- a/src/PhpPresentation/Writer/PowerPoint2007/AbstractPart.php +++ /dev/null @@ -1,77 +0,0 @@ -parentWriter = $pWriter; - } - - /** - * Get parent WriterInterface object - * - * @return \PhpOffice\PhpPresentation\Writer\WriterInterface - * @throws \Exception - */ - public function getParentWriter() - { - if (!is_null($this->parentWriter)) { - return $this->parentWriter; - } else { - throw new \Exception("No parent \PhpOffice\PhpPresentation\Writer\WriterInterface assigned."); - } - } - - /** - * Get XML writer - */ - protected function getXMLWriter() - { - $parentWriter = $this->getParentWriter(); - if (!$parentWriter instanceof PowerPoint2007) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\PowerPoint2007'); - } - if ($parentWriter->hasDiskCaching()) { - return new XMLWriter(XMLWriter::STORAGE_DISK, $parentWriter->getDiskCachingDirectory()); - } else { - return new XMLWriter(XMLWriter::STORAGE_MEMORY); - } - } -} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/Slide.php b/src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php similarity index 58% rename from src/PhpPresentation/Writer/PowerPoint2007/Slide.php rename to src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php index 8d7533775..44a5a316f 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/Slide.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php @@ -14,542 +14,211 @@ * @copyright 2009-2015 PHPPresentation contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ - namespace PhpOffice\PhpPresentation\Writer\PowerPoint2007; use PhpOffice\Common\Drawing as CommonDrawing; use PhpOffice\Common\Text; use PhpOffice\Common\XMLWriter; -use PhpOffice\PhpPresentation\Shape\AbstractDrawing; +use PhpOffice\PhpPresentation\Shape\AbstractGraphic; use PhpOffice\PhpPresentation\Shape\Chart as ShapeChart; +use PhpOffice\PhpPresentation\Shape\Comment; +use PhpOffice\PhpPresentation\Shape\Drawing\Gd as ShapeDrawingGd; +use PhpOffice\PhpPresentation\Shape\Drawing\File as ShapeDrawingFile; use PhpOffice\PhpPresentation\Shape\Group; use PhpOffice\PhpPresentation\Shape\Line; +use PhpOffice\PhpPresentation\Shape\Media; +use PhpOffice\PhpPresentation\Shape\Placeholder; use PhpOffice\PhpPresentation\Shape\RichText; use PhpOffice\PhpPresentation\Shape\RichText\BreakElement; use PhpOffice\PhpPresentation\Shape\RichText\Run; use PhpOffice\PhpPresentation\Shape\RichText\TextElement; -use PhpOffice\PhpPresentation\Shape\Table; -use PhpOffice\PhpPresentation\Slide as SlideElement; +use PhpOffice\PhpPresentation\Shape\Table as ShapeTable; +use PhpOffice\PhpPresentation\Slide; use PhpOffice\PhpPresentation\Slide\Note; -use PhpOffice\PhpPresentation\Slide\Transition; use PhpOffice\PhpPresentation\Style\Alignment; -use PhpOffice\PhpPresentation\Style\Border; use PhpOffice\PhpPresentation\Style\Bullet; -use PhpOffice\PhpPresentation\Style\Fill; +use PhpOffice\PhpPresentation\Style\Border; use PhpOffice\PhpPresentation\Style\Shadow; +use PhpOffice\PhpPresentation\Slide\AbstractSlide as AbstractSlideAlias; +use PhpOffice\PhpPresentation\Slide\SlideMaster; +use PhpOffice\PhpPresentation\Slide\Background; -/** - * Slide writer - */ -class Slide extends AbstractPart +abstract class AbstractSlide extends AbstractDecoratorWriter { /** - * Write slide to XML format - * - * @param \PhpOffice\PhpPresentation\Slide $pSlide - * @return string XML Output + * @param SlideMaster $pSlideMaster + * @param $objWriter + * @param $relId * @throws \Exception */ - public function writeSlide(SlideElement $pSlide = null) + protected function writeDrawingRelations(AbstractSlideAlias $pSlideMaster, $objWriter, $relId) { - // Check slide - if (is_null($pSlide)) { - throw new \Exception("Invalid \PhpOffice\PhpPresentation\Slide object passed."); - } - - // Create XML writer - $objWriter = $this->getXMLWriter(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // p:sld - $objWriter->startElement('p:sld'); - $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); - $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); - $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main'); - - // p:cSld - $objWriter->startElement('p:cSld'); - - // Background - if ($pSlide->getBackground() instanceof SlideElement\AbstractBackground) { - $oBackground = $pSlide->getBackground(); - // p:bg - $objWriter->startElement('p:bg'); - - // p:bgPr - $objWriter->startElement('p:bgPr'); - - if ($oBackground instanceof SlideElement\Background\Color) { - // a:solidFill - $objWriter->startElement('a:solidFill'); - - // a:srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $oBackground->getColor()->getRGB()); - $objWriter->endElement(); - - // > a:solidFill - $objWriter->endElement(); - } - - if ($oBackground instanceof SlideElement\Background\Image) { - // a:blipFill - $objWriter->startElement('a:blipFill'); - - // a:blip - $objWriter->startElement('a:blip'); - $objWriter->writeAttribute('r:embed', $oBackground->relationId); - - // > a:blipFill - $objWriter->endElement(); - - // a:stretch - $objWriter->startElement('a:stretch'); - - // a:fillRect - $objWriter->writeElement('a:fillRect'); - - // > a:stretch - $objWriter->endElement(); - - // > a:blipFill - $objWriter->endElement(); - } - - // > p:bgPr - $objWriter->endElement(); - - // > p:bg - $objWriter->endElement(); - } - - // p:spTree - $objWriter->startElement('p:spTree'); - - // p:nvGrpSpPr - $objWriter->startElement('p:nvGrpSpPr'); - - // p:cNvPr - $objWriter->startElement('p:cNvPr'); - $objWriter->writeAttribute('id', '1'); - $objWriter->writeAttribute('name', ''); - $objWriter->endElement(); - - // p:cNvGrpSpPr - $objWriter->writeElement('p:cNvGrpSpPr', null); - - // p:nvPr - $objWriter->writeElement('p:nvPr', null); - - $objWriter->endElement(); - - // p:grpSpPr - $objWriter->startElement('p:grpSpPr'); - - // a:xfrm - $objWriter->startElement('a:xfrm'); - - // a:off - $objWriter->startElement('a:off'); - $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pSlide->getOffsetX())); - $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($pSlide->getOffsetY())); - $objWriter->endElement(); // a:off - - // a:ext - $objWriter->startElement('a:ext'); - $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($pSlide->getExtentX())); - $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($pSlide->getExtentY())); - $objWriter->endElement(); // a:ext - - // a:chOff - $objWriter->startElement('a:chOff'); - $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pSlide->getOffsetX())); - $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($pSlide->getOffsetY())); - $objWriter->endElement(); // a:chOff - - // a:chExt - $objWriter->startElement('a:chExt'); - $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($pSlide->getExtentX())); - $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($pSlide->getExtentY())); - $objWriter->endElement(); // a:chExt - - $objWriter->endElement(); - - $objWriter->endElement(); - - // Loop shapes - $shapeId = 0; - $shapes = $pSlide->getShapeCollection(); - foreach ($shapes as $shape) { - // Increment $shapeId - ++$shapeId; - - // Check type - if ($shape instanceof RichText) { - $this->writeShapeText($objWriter, $shape, $shapeId); - } elseif ($shape instanceof Table) { - $this->writeShapeTable($objWriter, $shape, $shapeId); - } elseif ($shape instanceof Line) { - $this->writeShapeLine($objWriter, $shape, $shapeId); - } elseif ($shape instanceof ShapeChart) { - $this->writeShapeChart($objWriter, $shape, $shapeId); - } elseif ($shape instanceof AbstractDrawing) { - $this->writeShapePic($objWriter, $shape, $shapeId); - } elseif ($shape instanceof Group) { - $this->writeShapeGroup($objWriter, $shape, $shapeId); + if ($pSlideMaster->getShapeCollection()->count() > 0) { + // Loop trough images and write relationships + $iterator = $pSlideMaster->getShapeCollection()->getIterator(); + while ($iterator->valid()) { + if ($iterator->current() instanceof ShapeDrawingFile || $iterator->current() instanceof ShapeDrawingGd) { + // Write relationship for image drawing + $this->writeRelationship( + $objWriter, + $relId, + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', + '../media/' . str_replace(' ', '_', $iterator->current()->getIndexedFilename()) + ); + $iterator->current()->relationId = 'rId' . $relId; + ++$relId; + } elseif ($iterator->current() instanceof ShapeChart) { + // Write relationship for chart drawing + $this->writeRelationship( + $objWriter, + $relId, + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart', + '../charts/' . $iterator->current()->getIndexedFilename() + ); + $iterator->current()->relationId = 'rId' . $relId; + ++$relId; + } elseif ($iterator->current() instanceof Group) { + $iterator2 = $iterator->current()->getShapeCollection()->getIterator(); + while ($iterator2->valid()) { + if ($iterator2->current() instanceof ShapeDrawingFile || + $iterator2->current() instanceof ShapeDrawingGd + ) { + // Write relationship for image drawing + $this->writeRelationship( + $objWriter, + $relId, + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', + '../media/' . str_replace(' ', '_', $iterator2->current()->getIndexedFilename()) + ); + $iterator2->current()->relationId = 'rId' . $relId; + ++$relId; + } elseif ($iterator2->current() instanceof ShapeChart) { + // Write relationship for chart drawing + $this->writeRelationship( + $objWriter, + $relId, + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart', + '../charts/' . $iterator2->current()->getIndexedFilename() + ); + $iterator2->current()->relationId = 'rId' . $relId; + ++$relId; + } + $iterator2->next(); + } + } + $iterator->next(); } } - - // TODO - $objWriter->endElement(); - - $objWriter->endElement(); - - // p:clrMapOvr - $objWriter->startElement('p:clrMapOvr'); - - // a:masterClrMapping - $objWriter->writeElement('a:masterClrMapping', null); - - $objWriter->endElement(); - - if (!is_null($pSlide->getTransition())) { - $this->writeTransition($objWriter, $pSlide->getTransition()); - } - - $objWriter->endElement(); - - // Return - return $objWriter->getData(); } /** - * Write group - * - * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer - * @param \PhpOffice\PhpPresentation\Shape\Group $group - * @param int $shapeId + * @param XMLWriter $objWriter + * @param \ArrayObject|\PhpOffice\PhpPresentation\AbstractShape[] $shapes + * @param int $shapeId + * @throws \Exception */ - private function writeShapeGroup(XMLWriter $objWriter, Group $group, &$shapeId) + protected function writeShapeCollection(XMLWriter $objWriter, $shapes = array(), &$shapeId = 0) { - // p:grpSp - $objWriter->startElement('p:grpSp'); - - // p:nvGrpSpPr - $objWriter->startElement('p:nvGrpSpPr'); - - // p:cNvPr - $objWriter->startElement('p:cNvPr'); - $objWriter->writeAttribute('name', 'Group '.$shapeId++); - $objWriter->writeAttribute('id', $shapeId); - $objWriter->endElement(); // p:cNvPr - // NOTE: Re: $shapeId This seems to be how PowerPoint 2010 does business. - - // p:cNvGrpSpPr - $objWriter->writeElement('p:cNvGrpSpPr', null); - - // p:nvPr - $objWriter->writeElement('p:nvPr', null); - - $objWriter->endElement(); // p:nvGrpSpPr - - // p:grpSpPr - $objWriter->startElement('p:grpSpPr'); - - // a:xfrm - $objWriter->startElement('a:xfrm'); - - // a:off - $objWriter->startElement('a:off'); - $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($group->getOffsetX())); - $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($group->getOffsetY())); - $objWriter->endElement(); // a:off - - // a:ext - $objWriter->startElement('a:ext'); - $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($group->getExtentX())); - $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($group->getExtentY())); - $objWriter->endElement(); // a:ext - - // a:chOff - $objWriter->startElement('a:chOff'); - $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($group->getOffsetX())); - $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($group->getOffsetY())); - $objWriter->endElement(); // a:chOff - - // a:chExt - $objWriter->startElement('a:chExt'); - $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($group->getExtentX())); - $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($group->getExtentY())); - $objWriter->endElement(); // a:chExt - - $objWriter->endElement(); // a:xfrm - - $objWriter->endElement(); // p:grpSpPr - - $shapes = $group->getShapeCollection(); + if (count($shapes) == 0) { + return; + } foreach ($shapes as $shape) { // Increment $shapeId ++$shapeId; - // Check type if ($shape instanceof RichText) { $this->writeShapeText($objWriter, $shape, $shapeId); - } elseif ($shape instanceof Table) { + } elseif ($shape instanceof ShapeTable) { $this->writeShapeTable($objWriter, $shape, $shapeId); } elseif ($shape instanceof Line) { $this->writeShapeLine($objWriter, $shape, $shapeId); } elseif ($shape instanceof ShapeChart) { $this->writeShapeChart($objWriter, $shape, $shapeId); - } elseif ($shape instanceof AbstractDrawing) { + } elseif ($shape instanceof AbstractGraphic) { $this->writeShapePic($objWriter, $shape, $shapeId); } elseif ($shape instanceof Group) { $this->writeShapeGroup($objWriter, $shape, $shapeId); + } elseif ($shape instanceof Comment) { + return; + } else { + throw new \Exception("Unknown Shape type: {get_class($shape)}"); } } - - $objWriter->endElement(); // p:grpSp } /** - * Write chart + * Write txt * - * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer - * @param \PhpOffice\PhpPresentation\Shape\Chart $shape + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Shape\RichText $shape * @param int $shapeId + * @throws \Exception */ - private function writeShapeChart(XMLWriter $objWriter, ShapeChart $shape, $shapeId) + protected function writeShapeText(XMLWriter $objWriter, RichText $shape, $shapeId) { - // p:graphicFrame - $objWriter->startElement('p:graphicFrame'); - - // p:nvGraphicFramePr - $objWriter->startElement('p:nvGraphicFramePr'); - - // p:cNvPr + // p:sp + $objWriter->startElement('p:sp'); + // p:sp\p:nvSpPr + $objWriter->startElement('p:nvSpPr'); + // p:sp\p:nvSpPr\p:cNvPr $objWriter->startElement('p:cNvPr'); $objWriter->writeAttribute('id', $shapeId); - $objWriter->writeAttribute('name', $shape->getName()); - $objWriter->writeAttribute('descr', $shape->getDescription()); + if ($shape->isPlaceholder()) { + $objWriter->writeAttribute('name', 'Placeholder for ' . $shape->getPlaceholder()->getType()); + } else { + $objWriter->writeAttribute('name', ''); + } + // Hyperlink + if ($shape->hasHyperlink()) { + $this->writeHyperlink($objWriter, $shape); + } + // > p:sp\p:nvSpPr $objWriter->endElement(); - - // p:cNvGraphicFramePr - $objWriter->writeElement('p:cNvGraphicFramePr', null); - - // p:nvPr - $objWriter->writeElement('p:nvPr', null); - + // p:sp\p:cNvSpPr + $objWriter->startElement('p:cNvSpPr'); + $objWriter->writeAttribute('txBox', '1'); $objWriter->endElement(); - - // p:xfrm - $objWriter->startElement('p:xfrm'); - $objWriter->writeAttribute('rot', CommonDrawing::degreesToAngle($shape->getRotation())); - - // a:off + // p:sp\p:cNvSpPr\p:nvPr + if ($shape->isPlaceholder()) { + $objWriter->startElement('p:nvPr'); + $objWriter->startElement('p:ph'); + $objWriter->writeAttribute('type', $shape->getPlaceholder()->getType()); + if (!is_null($shape->getPlaceholder()->getIdx())) { + $objWriter->writeAttribute('idx', $shape->getPlaceholder()->getIdx()); + } + $objWriter->endElement(); + $objWriter->endElement(); + } else { + $objWriter->writeElement('p:nvPr', null); + } + // > p:sp\p:cNvSpPr + $objWriter->endElement(); + // p:sp\p:spPr + $objWriter->startElement('p:spPr'); + // p:sp\p:spPr\a:xfrm + $objWriter->startElement('a:xfrm'); + $objWriter->writeAttributeIf($shape->getRotation() != 0, 'rot', CommonDrawing::degreesToAngle($shape->getRotation())); + // p:sp\p:spPr\a:xfrm\a:off $objWriter->startElement('a:off'); $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX())); $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY())); $objWriter->endElement(); - - // a:ext + // p:sp\p:spPr\a:xfrm\a:ext $objWriter->startElement('a:ext'); $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth())); $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight())); $objWriter->endElement(); - + // > p:sp\p:spPr\a:xfrm $objWriter->endElement(); + // p:sp\p:spPr\a:prstGeom + $objWriter->startElement('a:prstGeom'); + $objWriter->writeAttribute('prst', 'rect'); - // a:graphic - $objWriter->startElement('a:graphic'); - - // a:graphicData - $objWriter->startElement('a:graphicData'); - $objWriter->writeAttribute('uri', 'http://schemas.openxmlformats.org/drawingml/2006/chart'); - - // c:chart - $objWriter->startElement('c:chart'); - $objWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart'); - $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); - $objWriter->writeAttribute('r:id', $shape->relationId); - $objWriter->endElement(); - - $objWriter->endElement(); - - $objWriter->endElement(); - - $objWriter->endElement(); - } - - /** - * Write pic - * - * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer - * @param \PhpOffice\PhpPresentation\Shape\AbstractDrawing $shape - * @param int $shapeId - * @throws \Exception - */ - private function writeShapePic(XMLWriter $objWriter, AbstractDrawing $shape, $shapeId) - { - // p:pic - $objWriter->startElement('p:pic'); - - // p:nvPicPr - $objWriter->startElement('p:nvPicPr'); - - // p:cNvPr - $objWriter->startElement('p:cNvPr'); - $objWriter->writeAttribute('id', $shapeId); - $objWriter->writeAttribute('name', $shape->getName()); - $objWriter->writeAttribute('descr', $shape->getDescription()); - - // a:hlinkClick - if ($shape->hasHyperlink()) { - $this->writeHyperlink($objWriter, $shape); - } - - $objWriter->endElement(); - - // p:cNvPicPr - $objWriter->startElement('p:cNvPicPr'); - - // a:picLocks - $objWriter->startElement('a:picLocks'); - $objWriter->writeAttribute('noChangeAspect', '1'); - $objWriter->endElement(); - - $objWriter->endElement(); - - // p:nvPr - $objWriter->writeElement('p:nvPr', null); - $objWriter->endElement(); - - // p:blipFill - $objWriter->startElement('p:blipFill'); - - // a:blip - $objWriter->startElement('a:blip'); - $objWriter->writeAttribute('r:embed', $shape->relationId); - $objWriter->endElement(); - - // a:stretch - $objWriter->startElement('a:stretch'); - $objWriter->writeElement('a:fillRect', null); - $objWriter->endElement(); - - $objWriter->endElement(); - - // p:spPr - $objWriter->startElement('p:spPr'); - - // a:xfrm - $objWriter->startElement('a:xfrm'); - $objWriter->writeAttribute('rot', CommonDrawing::degreesToAngle($shape->getRotation())); - - // a:off - $objWriter->startElement('a:off'); - $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX())); - $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY())); - $objWriter->endElement(); - - // a:ext - $objWriter->startElement('a:ext'); - $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth())); - $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight())); - $objWriter->endElement(); - - $objWriter->endElement(); - - // a:prstGeom - $objWriter->startElement('a:prstGeom'); - $objWriter->writeAttribute('prst', 'rect'); - - // a:avLst - $objWriter->writeElement('a:avLst', null); - - $objWriter->endElement(); - - if ($shape->getBorder()->getLineStyle() != Border::LINE_NONE) { - $this->writeBorder($objWriter, $shape->getBorder(), ''); - } - - if ($shape->getShadow()->isVisible()) { - $this->writeShadow($objWriter, $shape->getShadow()); - } - - $objWriter->endElement(); - - $objWriter->endElement(); - } - - /** - * Write txt - * - * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer - * @param \PhpOffice\PhpPresentation\Shape\RichText $shape - * @param int $shapeId - * @throws \Exception - */ - private function writeShapeText(XMLWriter $objWriter, RichText $shape, $shapeId) - { - // p:sp - $objWriter->startElement('p:sp'); - - // p:sp\p:nvSpPr - $objWriter->startElement('p:nvSpPr'); - - // p:sp\p:nvSpPr\p:cNvPr - $objWriter->startElement('p:cNvPr'); - $objWriter->writeAttribute('id', $shapeId); - $objWriter->writeAttribute('name', ''); - - // Hyperlink - if ($shape->hasHyperlink()) { - $this->writeHyperlink($objWriter, $shape); - } - // > p:sp\p:nvSpPr - $objWriter->endElement(); - - // p:sp\p:cNvSpPr - $objWriter->startElement('p:cNvSpPr'); - $objWriter->writeAttribute('txBox', '1'); - $objWriter->endElement(); - // p:sp\p:cNvSpPr\p:nvPr - $objWriter->writeElement('p:nvPr', null); - // > p:sp\p:cNvSpPr - $objWriter->endElement(); - - // p:sp\p:spPr - $objWriter->startElement('p:spPr'); - - // p:sp\p:spPr\a:xfrm - $objWriter->startElement('a:xfrm'); - $objWriter->writeAttribute('rot', CommonDrawing::degreesToAngle($shape->getRotation())); - - // p:sp\p:spPr\a:xfrm\a:off - $objWriter->startElement('a:off'); - $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX())); - $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY())); - $objWriter->endElement(); - - // p:sp\p:spPr\a:xfrm\a:ext - $objWriter->startElement('a:ext'); - $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth())); - $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight())); - $objWriter->endElement(); - - // > p:sp\p:spPr\a:xfrm - $objWriter->endElement(); + // p:sp\p:spPr\a:prstGeom\a:avLst + $objWriter->writeElement('a:avLst'); - // p:sp\p:spPr\a:prstGeom - $objWriter->startElement('a:prstGeom'); - $objWriter->writeAttribute('prst', 'rect'); $objWriter->endElement(); - - if ($shape->getFill()) { - $this->writeFill($objWriter, $shape->getFill()); - } + $this->writeFill($objWriter, $shape->getFill()); if ($shape->getBorder()->getLineStyle() != Border::LINE_NONE) { $this->writeBorder($objWriter, $shape->getBorder(), ''); } @@ -558,10 +227,8 @@ private function writeShapeText(XMLWriter $objWriter, RichText $shape, $shapeId) } // > p:sp\p:spPr $objWriter->endElement(); - // p:txBody $objWriter->startElement('p:txBody'); - // a:bodyPr //@link :http://msdn.microsoft.com/en-us/library/documentformat.openxml.drawing.bodyproperties%28v=office.14%29.aspx $objWriter->startElement('a:bodyPr'); @@ -573,30 +240,25 @@ private function writeShapeText(XMLWriter $objWriter, RichText $shape, $shapeId) $objWriter->writeAttribute('wrap', $shape->getWrap()); } $objWriter->writeAttribute('rtlCol', '0'); - if ($shape->getHorizontalOverflow() != RichText::OVERFLOW_OVERFLOW) { $objWriter->writeAttribute('horzOverflow', $shape->getHorizontalOverflow()); } if ($shape->getVerticalOverflow() != RichText::OVERFLOW_OVERFLOW) { $objWriter->writeAttribute('vertOverflow', $shape->getVerticalOverflow()); } - if ($shape->isUpright()) { $objWriter->writeAttribute('upright', '1'); } if ($shape->isVertical()) { $objWriter->writeAttribute('vert', 'vert'); } - $objWriter->writeAttribute('bIns', CommonDrawing::pixelsToEmu($shape->getInsetBottom())); $objWriter->writeAttribute('lIns', CommonDrawing::pixelsToEmu($shape->getInsetLeft())); $objWriter->writeAttribute('rIns', CommonDrawing::pixelsToEmu($shape->getInsetRight())); $objWriter->writeAttribute('tIns', CommonDrawing::pixelsToEmu($shape->getInsetTop())); - if ($shape->getColumns() <> 1) { $objWriter->writeAttribute('numCol', $shape->getColumns()); } - // a:spAutoFit $objWriter->startElement('a:' . $shape->getAutoFit()); if ($shape->getAutoFit() == RichText::AUTOFIT_NORMAL) { @@ -607,19 +269,28 @@ private function writeShapeText(XMLWriter $objWriter, RichText $shape, $shapeId) $objWriter->writeAttribute('lnSpcReduction', (int)($shape->getLineSpaceReduction() * 1000)); } } - $objWriter->endElement(); - $objWriter->endElement(); - // a:lstStyle $objWriter->writeElement('a:lstStyle', null); - - // Write paragraphs - $this->writeParagraphs($objWriter, $shape->getParagraphs()); - + if ($shape->isPlaceholder() && + ($shape->getPlaceholder()->getType() == Placeholder::PH_TYPE_SLIDENUM || + $shape->getPlaceholder()->getType() == Placeholder::PH_TYPE_DATETIME) + ) { + $objWriter->startElement('a:p'); + $objWriter->startElement('a:fld'); + $objWriter->writeAttribute('id', $this->getGUID()); + $objWriter->writeAttribute('type', ( + $shape->getPlaceholder()->getType() == Placeholder::PH_TYPE_SLIDENUM ? 'slidenum' : 'datetime')); + $objWriter->writeElement('a:t', ( + $shape->getPlaceholder()->getType() == Placeholder::PH_TYPE_SLIDENUM ? '' : '03-04-05')); + $objWriter->endElement(); + $objWriter->endElement(); + } else { + // Write paragraphs + $this->writeParagraphs($objWriter, $shape->getParagraphs()); + } $objWriter->endElement(); - $objWriter->endElement(); } @@ -627,128 +298,99 @@ private function writeShapeText(XMLWriter $objWriter, RichText $shape, $shapeId) * Write table * * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer - * @param \PhpOffice\PhpPresentation\Shape\Table $shape - * @param int $shapeId + * @param \PhpOffice\PhpPresentation\Shape\Table $shape + * @param int $shapeId * @throws \Exception */ - private function writeShapeTable(XMLWriter $objWriter, Table $shape, $shapeId) + protected function writeShapeTable(XMLWriter $objWriter, ShapeTable $shape, $shapeId) { // p:graphicFrame $objWriter->startElement('p:graphicFrame'); - // p:nvGraphicFramePr $objWriter->startElement('p:nvGraphicFramePr'); - // p:cNvPr $objWriter->startElement('p:cNvPr'); $objWriter->writeAttribute('id', $shapeId); $objWriter->writeAttribute('name', $shape->getName()); $objWriter->writeAttribute('descr', $shape->getDescription()); - $objWriter->endElement(); - // p:cNvGraphicFramePr $objWriter->startElement('p:cNvGraphicFramePr'); - // a:graphicFrameLocks $objWriter->startElement('a:graphicFrameLocks'); $objWriter->writeAttribute('noGrp', '1'); $objWriter->endElement(); - $objWriter->endElement(); - // p:nvPr $objWriter->writeElement('p:nvPr', null); - $objWriter->endElement(); - // p:xfrm $objWriter->startElement('p:xfrm'); - // a:off $objWriter->startElement('a:off'); $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX())); $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY())); $objWriter->endElement(); - // a:ext $objWriter->startElement('a:ext'); $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth())); $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight())); $objWriter->endElement(); - $objWriter->endElement(); - // a:graphic $objWriter->startElement('a:graphic'); - // a:graphicData $objWriter->startElement('a:graphicData'); $objWriter->writeAttribute('uri', 'http://schemas.openxmlformats.org/drawingml/2006/table'); - // a:tbl $objWriter->startElement('a:tbl'); - // a:tblPr $objWriter->startElement('a:tblPr'); $objWriter->writeAttribute('firstRow', '1'); $objWriter->writeAttribute('bandRow', '1'); - $objWriter->endElement(); - // a:tblGrid $objWriter->startElement('a:tblGrid'); - // Write cell widths $countCells = count($shape->getRow(0)->getCells()); for ($cell = 0; $cell < $countCells; $cell++) { // a:gridCol $objWriter->startElement('a:gridCol'); - // Calculate column width $width = $shape->getRow(0)->getCell($cell)->getWidth(); if ($width == 0) { - $colCount = count($shape->getRow(0)->getCells()); + $colCount = count($shape->getRow(0)->getCells()); $totalWidth = $shape->getWidth(); - $width = $totalWidth / $colCount; + $width = $totalWidth / $colCount; } - $objWriter->writeAttribute('w', CommonDrawing::pixelsToEmu($width)); $objWriter->endElement(); } - $objWriter->endElement(); - // Colspan / rowspan containers $colSpan = array(); $rowSpan = array(); - // Default border style $defaultBorder = new Border(); - // Write rows $countRows = count($shape->getRows()); for ($row = 0; $row < $countRows; $row++) { // a:tr $objWriter->startElement('a:tr'); $objWriter->writeAttribute('h', CommonDrawing::pixelsToEmu($shape->getRow($row)->getHeight())); - // Write cells $countCells = count($shape->getRow($row)->getCells()); for ($cell = 0; $cell < $countCells; $cell++) { // Current cell $currentCell = $shape->getRow($row)->getCell($cell); - // Next cell right $nextCellRight = $shape->getRow($row)->getCell($cell + 1, true); - // Next cell below - $nextRowBelow = $shape->getRow($row + 1, true); + $nextRowBelow = $shape->getRow($row + 1, true); $nextCellBelow = null; if ($nextRowBelow != null) { $nextCellBelow = $nextRowBelow->getCell($cell, true); } - // a:tc $objWriter->startElement('a:tc'); // Colspan @@ -759,7 +401,6 @@ private function writeShapeTable(XMLWriter $objWriter, Table $shape, $shapeId) $colSpan[$row]--; $objWriter->writeAttribute('hMerge', '1'); } - // Rowspan if ($currentCell->getRowSpan() > 1) { $objWriter->writeAttribute('rowSpan', $currentCell->getRowSpan()); @@ -768,53 +409,46 @@ private function writeShapeTable(XMLWriter $objWriter, Table $shape, $shapeId) $rowSpan[$cell]--; $objWriter->writeAttribute('vMerge', '1'); } - // a:txBody $objWriter->startElement('a:txBody'); - // a:bodyPr $objWriter->startElement('a:bodyPr'); $objWriter->writeAttribute('wrap', 'square'); $objWriter->writeAttribute('rtlCol', '0'); - // a:spAutoFit $objWriter->writeElement('a:spAutoFit', null); - $objWriter->endElement(); - // a:lstStyle $objWriter->writeElement('a:lstStyle', null); - // Write paragraphs $this->writeParagraphs($objWriter, $currentCell->getParagraphs()); - $objWriter->endElement(); - // a:tcPr $objWriter->startElement('a:tcPr'); // Alignment (horizontal) - $firstParagraph = $currentCell->getParagraph(0); + $firstParagraph = $currentCell->getParagraph(0); $verticalAlign = $firstParagraph->getAlignment()->getVertical(); if ($verticalAlign != Alignment::VERTICAL_BASE && $verticalAlign != Alignment::VERTICAL_AUTO) { $objWriter->writeAttribute('anchor', $verticalAlign); } - // Determine borders - $borderLeft = $currentCell->getBorders()->getLeft(); - $borderRight = $currentCell->getBorders()->getRight(); - $borderTop = $currentCell->getBorders()->getTop(); - $borderBottom = $currentCell->getBorders()->getBottom(); + $borderLeft = $currentCell->getBorders()->getLeft(); + $borderRight = $currentCell->getBorders()->getRight(); + $borderTop = $currentCell->getBorders()->getTop(); + $borderBottom = $currentCell->getBorders()->getBottom(); $borderDiagonalDown = $currentCell->getBorders()->getDiagonalDown(); - $borderDiagonalUp = $currentCell->getBorders()->getDiagonalUp(); - + $borderDiagonalUp = $currentCell->getBorders()->getDiagonalUp(); // Fix PowerPoint implementation - if (!is_null($nextCellRight) && $nextCellRight->getBorders()->getRight()->getHashCode() != $defaultBorder->getHashCode()) { + if (!is_null($nextCellRight) + && $nextCellRight->getBorders()->getRight()->getHashCode() != $defaultBorder->getHashCode() + ) { $borderRight = $nextCellRight->getBorders()->getLeft(); } - if (!is_null($nextCellBelow) && $nextCellBelow->getBorders()->getBottom()->getHashCode() != $defaultBorder->getHashCode()) { + if (!is_null($nextCellBelow) + && $nextCellBelow->getBorders()->getBottom()->getHashCode() != $defaultBorder->getHashCode() + ) { $borderBottom = $nextCellBelow->getBorders()->getTop(); } - // Write borders $this->writeBorder($objWriter, $borderLeft, 'L'); $this->writeBorder($objWriter, $borderRight, 'R'); @@ -822,57 +456,48 @@ private function writeShapeTable(XMLWriter $objWriter, Table $shape, $shapeId) $this->writeBorder($objWriter, $borderBottom, 'B'); $this->writeBorder($objWriter, $borderDiagonalDown, 'TlToBr'); $this->writeBorder($objWriter, $borderDiagonalUp, 'BlToTr'); - // Fill $this->writeFill($objWriter, $currentCell->getFill()); - $objWriter->endElement(); - $objWriter->endElement(); } - $objWriter->endElement(); } - $objWriter->endElement(); - $objWriter->endElement(); - $objWriter->endElement(); - $objWriter->endElement(); } /** * Write paragraphs * - * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer * @param \PhpOffice\PhpPresentation\Shape\RichText\Paragraph[] $paragraphs * @throws \Exception */ - private function writeParagraphs(XMLWriter $objWriter, $paragraphs) + protected function writeParagraphs(XMLWriter $objWriter, $paragraphs) { // Loop trough paragraphs foreach ($paragraphs as $paragraph) { // a:p $objWriter->startElement('a:p'); - // a:pPr $objWriter->startElement('a:pPr'); $objWriter->writeAttribute('algn', $paragraph->getAlignment()->getHorizontal()); $objWriter->writeAttribute('fontAlgn', $paragraph->getAlignment()->getVertical()); $objWriter->writeAttribute('marL', CommonDrawing::pixelsToEmu($paragraph->getAlignment()->getMarginLeft())); - $objWriter->writeAttribute('marR', CommonDrawing::pixelsToEmu($paragraph->getAlignment()->getMarginRight())); + $objWriter->writeAttribute('marR', CommonDrawing::pixelsToEmu( + $paragraph->getAlignment()->getMarginRight() + )); $objWriter->writeAttribute('indent', CommonDrawing::pixelsToEmu($paragraph->getAlignment()->getIndent())); $objWriter->writeAttribute('lvl', $paragraph->getAlignment()->getLevel()); - // Bullet type specified? if ($paragraph->getBulletStyle()->getBulletType() != Bullet::TYPE_NONE) { // a:buFont $objWriter->startElement('a:buFont'); $objWriter->writeAttribute('typeface', $paragraph->getBulletStyle()->getBulletFont()); $objWriter->endElement(); - if ($paragraph->getBulletStyle()->getBulletType() == Bullet::TYPE_BULLET) { // a:buChar $objWriter->startElement('a:buChar'); @@ -888,9 +513,7 @@ private function writeParagraphs(XMLWriter $objWriter, $paragraphs) $objWriter->endElement(); } } - $objWriter->endElement(); - // Loop trough rich text elements $elements = $paragraph->getRichTextElements(); foreach ($elements as $element) { @@ -900,27 +523,24 @@ private function writeParagraphs(XMLWriter $objWriter, $paragraphs) } elseif ($element instanceof Run || $element instanceof TextElement) { // a:r $objWriter->startElement('a:r'); - // a:rPr if ($element instanceof Run) { // a:rPr $objWriter->startElement('a:rPr'); - + // Lang + $objWriter->writeAttribute('lang', ($element->getLanguage() ? + $element->getLanguage() : 'en-US')); // Bold $objWriter->writeAttribute('b', ($element->getFont()->isBold() ? '1' : '0')); - // Italic $objWriter->writeAttribute('i', ($element->getFont()->isItalic() ? '1' : '0')); - // Strikethrough - $objWriter->writeAttribute('strike', ($element->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); - + $objWriter->writeAttribute('strike', ($element->getFont()->isStrikethrough() ? + 'sngStrike' : 'noStrike')); // Size $objWriter->writeAttribute('sz', ($element->getFont()->getSize() * 100)); - // Underline $objWriter->writeAttribute('u', $element->getFont()->getUnderline()); - // Superscript / subscript if ($element->getFont()->isSuperScript() || $element->getFont()->isSubScript()) { if ($element->getFont()->isSuperScript()) { @@ -929,39 +549,30 @@ private function writeParagraphs(XMLWriter $objWriter, $paragraphs) $objWriter->writeAttribute('baseline', '-25000'); } } - // Color - a:solidFill $objWriter->startElement('a:solidFill'); - // a:srgbClr $objWriter->startElement('a:srgbClr'); $objWriter->writeAttribute('val', $element->getFont()->getColor()->getRGB()); $objWriter->endElement(); - $objWriter->endElement(); - // Font - a:latin $objWriter->startElement('a:latin'); $objWriter->writeAttribute('typeface', $element->getFont()->getName()); $objWriter->endElement(); - // a:hlinkClick if ($element->hasHyperlink()) { $this->writeHyperlink($objWriter, $element); } - $objWriter->endElement(); } - // t $objWriter->startElement('a:t'); $objWriter->writeCData(Text::controlCharacterPHP2OOXML($element->getText())); $objWriter->endElement(); - $objWriter->endElement(); } } - $objWriter->endElement(); } } @@ -973,42 +584,32 @@ private function writeParagraphs(XMLWriter $objWriter, $paragraphs) * @param \PhpOffice\PhpPresentation\Shape\Line $shape * @param int $shapeId */ - private function writeShapeLine(XMLWriter $objWriter, Line $shape, $shapeId) + protected function writeShapeLine(XMLWriter $objWriter, Line $shape, $shapeId) { // p:sp $objWriter->startElement('p:cxnSp'); - // p:nvSpPr $objWriter->startElement('p:nvCxnSpPr'); - // p:cNvPr $objWriter->startElement('p:cNvPr'); $objWriter->writeAttribute('id', $shapeId); $objWriter->writeAttribute('name', ''); - $objWriter->endElement(); - // p:cNvCxnSpPr $objWriter->writeElement('p:cNvCxnSpPr', null); - // p:nvPr $objWriter->writeElement('p:nvPr', null); - $objWriter->endElement(); - // p:spPr $objWriter->startElement('p:spPr'); - // a:xfrm $objWriter->startElement('a:xfrm'); - if ($shape->getWidth() >= 0 && $shape->getHeight() >= 0) { // a:off $objWriter->startElement('a:off'); $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX())); $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY())); $objWriter->endElement(); - // a:ext $objWriter->startElement('a:ext'); $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth())); @@ -1020,7 +621,6 @@ private function writeShapeLine(XMLWriter $objWriter, Line $shape, $shapeId) $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX() + $shape->getWidth())); $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY() + $shape->getHeight())); $objWriter->endElement(); - // a:ext $objWriter->startElement('a:ext'); $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu(-$shape->getWidth())); @@ -1028,13 +628,11 @@ private function writeShapeLine(XMLWriter $objWriter, Line $shape, $shapeId) $objWriter->endElement(); } elseif ($shape->getHeight() < 0) { $objWriter->writeAttribute('flipV', 1); - // a:off $objWriter->startElement('a:off'); $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX())); $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY() + $shape->getHeight())); $objWriter->endElement(); - // a:ext $objWriter->startElement('a:ext'); $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth())); @@ -1042,240 +640,51 @@ private function writeShapeLine(XMLWriter $objWriter, Line $shape, $shapeId) $objWriter->endElement(); } elseif ($shape->getWidth() < 0) { $objWriter->writeAttribute('flipV', 1); - // a:off $objWriter->startElement('a:off'); $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX() + $shape->getWidth())); $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY())); $objWriter->endElement(); - // a:ext $objWriter->startElement('a:ext'); $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu(-$shape->getWidth())); $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight())); $objWriter->endElement(); } - $objWriter->endElement(); - // a:prstGeom $objWriter->startElement('a:prstGeom'); $objWriter->writeAttribute('prst', 'line'); - $objWriter->endElement(); + // a:prstGeom/a:avLst + $objWriter->writeElement('a:avLst'); + + $objWriter->endElement(); if ($shape->getBorder()->getLineStyle() != Border::LINE_NONE) { $this->writeBorder($objWriter, $shape->getBorder(), ''); } - $objWriter->endElement(); - $objWriter->endElement(); } /** - * Write Border - * - * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer - * @param \PhpOffice\PhpPresentation\Style\Border $pBorder Border - * @param string $pElementName Element name - * @throws \Exception - */ - protected function writeBorder(XMLWriter $objWriter, Border $pBorder, $pElementName = 'L') - { - // Line style - $lineStyle = $pBorder->getLineStyle(); - if ($lineStyle == Border::LINE_NONE) { - $lineStyle = Border::LINE_SINGLE; - } - - // Line width - $lineWidth = 12700 * $pBorder->getLineWidth(); - - // a:ln $pElementName - $objWriter->startElement('a:ln' . $pElementName); - $objWriter->writeAttribute('w', $lineWidth); - $objWriter->writeAttribute('cap', 'flat'); - $objWriter->writeAttribute('cmpd', $lineStyle); - $objWriter->writeAttribute('algn', 'ctr'); - - // Fill? - if ($pBorder->getLineStyle() == Border::LINE_NONE) { - // a:noFill - $objWriter->writeElement('a:noFill', null); - } else { - // a:solidFill - $objWriter->startElement('a:solidFill'); - - // a:srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $pBorder->getColor()->getRGB()); - $objWriter->endElement(); - - $objWriter->endElement(); - } - - // Dash - $objWriter->startElement('a:prstDash'); - $objWriter->writeAttribute('val', $pBorder->getDashStyle()); - $objWriter->endElement(); - - // a:round - $objWriter->writeElement('a:round', null); - - // a:headEnd - $objWriter->startElement('a:headEnd'); - $objWriter->writeAttribute('type', 'none'); - $objWriter->writeAttribute('w', 'med'); - $objWriter->writeAttribute('len', 'med'); - $objWriter->endElement(); - - // a:tailEnd - $objWriter->startElement('a:tailEnd'); - $objWriter->writeAttribute('type', 'none'); - $objWriter->writeAttribute('w', 'med'); - $objWriter->writeAttribute('len', 'med'); - $objWriter->endElement(); - - $objWriter->endElement(); - } - - /** - * Write Fill - * - * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer - * @param \PhpOffice\PhpPresentation\Style\Fill $pFill Fill style - * @throws \Exception + * Write Shadow + * @param XMLWriter $objWriter + * @param Shadow $oShadow */ - protected function writeFill(XMLWriter $objWriter, Fill $pFill) + protected function writeShadow(XMLWriter $objWriter, $oShadow) { - // Is it a fill? - if ($pFill->getFillType() == Fill::FILL_NONE) { + if (!($oShadow instanceof Shadow)) { return; } - // Is it a solid fill? - if ($pFill->getFillType() == Fill::FILL_SOLID) { - $this->writeSolidFill($objWriter, $pFill); + if (!$oShadow->isVisible()) { return; } - // Check if this is a pattern type or gradient type - if ($pFill->getFillType() == Fill::FILL_GRADIENT_LINEAR || $pFill->getFillType() == Fill::FILL_GRADIENT_PATH) { - // Gradient fill - $this->writeGradientFill($objWriter, $pFill); - } else { - // Pattern fill - $this->writePatternFill($objWriter, $pFill); - } - } - - /** - * Write Solid Fill - * - * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer - * @param \PhpOffice\PhpPresentation\Style\Fill $pFill Fill style - * @throws \Exception - */ - protected function writeSolidFill(XMLWriter $objWriter, Fill $pFill) - { - // a:gradFill - $objWriter->startElement('a:solidFill'); - - // srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $pFill->getStartColor()->getRGB()); - $objWriter->endElement(); - - $objWriter->endElement(); - } - - /** - * Write Gradient Fill - * - * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer - * @param \PhpOffice\PhpPresentation\Style\Fill $pFill Fill style - * @throws \Exception - */ - protected function writeGradientFill(XMLWriter $objWriter, Fill $pFill) - { - // a:gradFill - $objWriter->startElement('a:gradFill'); - - // a:gsLst - $objWriter->startElement('a:gsLst'); - // a:gs - $objWriter->startElement('a:gs'); - $objWriter->writeAttribute('pos', '0'); - - // srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $pFill->getStartColor()->getRGB()); - $objWriter->endElement(); - - $objWriter->endElement(); - - // a:gs - $objWriter->startElement('a:gs'); - $objWriter->writeAttribute('pos', '100000'); - - // srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $pFill->getEndColor()->getRGB()); - $objWriter->endElement(); - - $objWriter->endElement(); - - $objWriter->endElement(); - - // a:lin - $objWriter->startElement('a:lin'); - $objWriter->writeAttribute('ang', CommonDrawing::degreesToAngle($pFill->getRotation())); - $objWriter->writeAttribute('scaled', '0'); - $objWriter->endElement(); - - $objWriter->endElement(); - } - - /** - * Write Pattern Fill - * - * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer - * @param \PhpOffice\PhpPresentation\Style\Fill $pFill Fill style - * @throws \Exception - */ - protected function writePatternFill(XMLWriter $objWriter, Fill $pFill) - { - // a:pattFill - $objWriter->startElement('a:pattFill'); - - // fgClr - $objWriter->startElement('a:fgClr'); - - // srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $pFill->getStartColor()->getRGB()); - $objWriter->endElement(); - - $objWriter->endElement(); - - // bgClr - $objWriter->startElement('a:bgClr'); - - // srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $pFill->getEndColor()->getRGB()); - $objWriter->endElement(); - - $objWriter->endElement(); - - $objWriter->endElement(); - } - - protected function writeShadow(XMLWriter $objWriter, Shadow $oShadow) - { // a:effectLst $objWriter->startElement('a:effectLst'); - + // a:outerShdw $objWriter->startElement('a:outerShdw'); $objWriter->writeAttribute('blurRad', CommonDrawing::pixelsToEmu($oShadow->getBlurRadius())); @@ -1283,30 +692,21 @@ protected function writeShadow(XMLWriter $objWriter, Shadow $oShadow) $objWriter->writeAttribute('dir', CommonDrawing::degreesToAngle($oShadow->getDirection())); $objWriter->writeAttribute('algn', $oShadow->getAlignment()); $objWriter->writeAttribute('rotWithShape', '0'); - - // a:srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $oShadow->getColor()->getRGB()); - - // a:alpha - $objWriter->startElement('a:alpha'); - $objWriter->writeAttribute('val', $oShadow->getAlpha() * 1000); - $objWriter->endElement(); - - $objWriter->endElement(); - + + $this->writeColor($objWriter, $oShadow->getColor(), $oShadow->getAlpha()); + $objWriter->endElement(); - + $objWriter->endElement(); } - + /** * Write hyperlink * - * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer * @param \PhpOffice\PhpPresentation\AbstractShape|\PhpOffice\PhpPresentation\Shape\RichText\TextElement $shape */ - private function writeHyperlink(XMLWriter $objWriter, $shape) + protected function writeHyperlink(XMLWriter $objWriter, $shape) { // a:hlinkClick $objWriter->startElement('a:hlinkClick'); @@ -1322,132 +722,97 @@ private function writeHyperlink(XMLWriter $objWriter, $shape) * Write Note Slide * @param Note $pNote * @throws \Exception + * @return string */ - public function writeNote(Note $pNote = null) + protected function writeNote(Note $pNote) { - // Check slide - if (is_null($pNote)) { - throw new \Exception("Invalid \PhpOffice\PhpPresentation\Slide\Note object passed."); - } - // Create XML writer - $objWriter = $this->getXMLWriter(); - + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); // XML header $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - // p:notes $objWriter->startElement('p:notes'); $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main'); $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); - // p:cSld $objWriter->startElement('p:cSld'); - // p:spTree $objWriter->startElement('p:spTree'); - // p:nvGrpSpPr $objWriter->startElement('p:nvGrpSpPr'); - // p:cNvPr $objWriter->startElement('p:cNvPr'); $objWriter->writeAttribute('id', '1'); $objWriter->writeAttribute('name', ''); $objWriter->endElement(); - // p:cNvGrpSpPr $objWriter->writeElement('p:cNvGrpSpPr', null); - // p:nvPr $objWriter->writeElement('p:nvPr', null); - // ## p:nvGrpSpPr $objWriter->endElement(); - // p:grpSpPr $objWriter->startElement('p:grpSpPr'); - // a:xfrm $objWriter->startElement('a:xfrm'); - // a:off $objWriter->startElement('a:off'); $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pNote->getOffsetX())); $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($pNote->getOffsetY())); $objWriter->endElement(); // a:off - // a:ext $objWriter->startElement('a:ext'); $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($pNote->getExtentX())); $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($pNote->getExtentY())); $objWriter->endElement(); // a:ext - // a:chOff $objWriter->startElement('a:chOff'); $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pNote->getOffsetX())); $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($pNote->getOffsetY())); $objWriter->endElement(); // a:chOff - // a:chExt $objWriter->startElement('a:chExt'); $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($pNote->getExtentX())); $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($pNote->getExtentY())); $objWriter->endElement(); // a:chExt - // ## a:xfrm $objWriter->endElement(); - // ## p:grpSpPr $objWriter->endElement(); - // p:sp $objWriter->startElement('p:sp'); - // p:nvSpPr $objWriter->startElement('p:nvSpPr'); - $objWriter->startElement('p:cNvPr'); $objWriter->writeAttribute('id', '1'); $objWriter->writeAttribute('name', 'Notes Placeholder'); $objWriter->endElement(); - // p:cNvSpPr $objWriter->startElement('p:cNvSpPr'); - //a:spLocks $objWriter->startElement('a:spLocks'); $objWriter->writeAttribute('noGrp', '1'); $objWriter->endElement(); - // ## p:cNvSpPr $objWriter->endElement(); - // p:nvPr $objWriter->startElement('p:nvPr'); - $objWriter->startElement('p:ph'); $objWriter->writeAttribute('type', 'body'); $objWriter->writeAttribute('idx', '1'); $objWriter->endElement(); - // ## p:nvPr $objWriter->endElement(); - // ## p:nvSpPr $objWriter->endElement(); - $objWriter->writeElement('p:spPr', null); - // p:txBody $objWriter->startElement('p:txBody'); - $objWriter->writeElement('a:bodyPr', null); $objWriter->writeElement('a:lstStyle', null); - // Loop shapes - $shapes = $pNote->getShapeCollection(); + $shapes = $pNote->getShapeCollection(); foreach ($shapes as $shape) { // Check type if ($shape instanceof RichText) { @@ -1455,274 +820,318 @@ public function writeNote(Note $pNote = null) $this->writeParagraphs($objWriter, $paragraphs); } } - // ## p:txBody $objWriter->endElement(); - // ## p:sp $objWriter->endElement(); - // ## p:spTree $objWriter->endElement(); - // ## p:cSld $objWriter->endElement(); - // ## p:notes $objWriter->endElement(); - // Return return $objWriter->getData(); } /** - * Write Transition Slide - * @link http://officeopenxml.com/prSlide-transitions.php - * @param XMLWriter $objWriter - * @param Transition $transition + * Write chart + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Shape\Chart $shape + * @param int $shapeId + */ + protected function writeShapeChart(XMLWriter $objWriter, ShapeChart $shape, $shapeId) + { + // p:graphicFrame + $objWriter->startElement('p:graphicFrame'); + // p:nvGraphicFramePr + $objWriter->startElement('p:nvGraphicFramePr'); + // p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('id', $shapeId); + $objWriter->writeAttribute('name', $shape->getName()); + $objWriter->writeAttribute('descr', $shape->getDescription()); + $objWriter->endElement(); + // p:cNvGraphicFramePr + $objWriter->writeElement('p:cNvGraphicFramePr', null); + // p:nvPr + $objWriter->writeElement('p:nvPr', null); + $objWriter->endElement(); + // p:xfrm + $objWriter->startElement('p:xfrm'); + $objWriter->writeAttributeIf($shape->getRotation() != 0, 'rot', CommonDrawing::degreesToAngle($shape->getRotation())); + // a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY())); + $objWriter->endElement(); + // a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight())); + $objWriter->endElement(); + $objWriter->endElement(); + // a:graphic + $objWriter->startElement('a:graphic'); + // a:graphicData + $objWriter->startElement('a:graphicData'); + $objWriter->writeAttribute('uri', 'http://schemas.openxmlformats.org/drawingml/2006/chart'); + // c:chart + $objWriter->startElement('c:chart'); + $objWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart'); + $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $objWriter->writeAttribute('r:id', $shape->relationId); + $objWriter->endElement(); + $objWriter->endElement(); + $objWriter->endElement(); + $objWriter->endElement(); + } + + /** + * Write pic + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Shape\AbstractGraphic $shape + * @param int $shapeId + * @throws \Exception */ - public function writeTransition(XMLWriter $objWriter, Transition $transition) + protected function writeShapePic(XMLWriter $objWriter, AbstractGraphic $shape, $shapeId) { - $objWriter->startElement('p:transition'); - if (!is_null($transition->getSpeed())) { - $objWriter->writeAttribute('spd', $transition->getSpeed()); + // p:pic + $objWriter->startElement('p:pic'); + // p:nvPicPr + $objWriter->startElement('p:nvPicPr'); + // p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('id', $shapeId); + $objWriter->writeAttribute('name', $shape->getName()); + $objWriter->writeAttribute('descr', $shape->getDescription()); + // a:hlinkClick + if ($shape->hasHyperlink()) { + $this->writeHyperlink($objWriter, $shape); } - $objWriter->writeAttribute('advClick', $transition->hasManualTrigger() ? '1' : '0'); - if ($transition->hasTimeTrigger()) { - $objWriter->writeAttribute('advTm', $transition->getAdvanceTimeTrigger()); + $objWriter->endElement(); + // p:cNvPicPr + $objWriter->startElement('p:cNvPicPr'); + // a:picLocks + $objWriter->startElement('a:picLocks'); + $objWriter->writeAttribute('noChangeAspect', '1'); + $objWriter->endElement(); + $objWriter->endElement(); + // p:nvPr + $objWriter->startElement('p:nvPr'); + /** + * @link : https://github.com/stefslon/exportToPPTX/blob/master/exportToPPTX.m#L2128 + */ + if ($shape instanceof Media) { + // p:nvPr > a:videoFile + $objWriter->startElement('a:videoFile'); + $objWriter->writeAttribute('r:link', $shape->relationId); + $objWriter->endElement(); + // p:nvPr > p:extLst + $objWriter->startElement('p:extLst'); + // p:nvPr > p:extLst > p:ext + $objWriter->startElement('p:ext'); + $objWriter->writeAttribute('uri', '{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}'); + // p:nvPr > p:extLst > p:ext > p14:media + $objWriter->startElement('p14:media'); + $objWriter->writeAttribute('r:embed', $shape->relationId); + $objWriter->writeAttribute('xmlns:p14', 'http://schemas.microsoft.com/office/powerpoint/2010/main'); + // p:nvPr > p:extLst > p:ext > ##p14:media + $objWriter->endElement(); + // p:nvPr > p:extLst > ##p:ext + $objWriter->endElement(); + // p:nvPr > ##p:extLst + $objWriter->endElement(); } + // ##p:nvPr + $objWriter->endElement(); + $objWriter->endElement(); + // p:blipFill + $objWriter->startElement('p:blipFill'); + // a:blip + $objWriter->startElement('a:blip'); + $objWriter->writeAttribute('r:embed', $shape->relationId); + $objWriter->endElement(); + // a:stretch + $objWriter->startElement('a:stretch'); + $objWriter->writeElement('a:fillRect', null); + $objWriter->endElement(); + $objWriter->endElement(); + // p:spPr + $objWriter->startElement('p:spPr'); + // a:xfrm + $objWriter->startElement('a:xfrm'); + $objWriter->writeAttributeIf($shape->getRotation() != 0, 'rot', CommonDrawing::degreesToAngle($shape->getRotation())); + // a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY())); + $objWriter->endElement(); + // a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight())); + $objWriter->endElement(); + $objWriter->endElement(); + // a:prstGeom + $objWriter->startElement('a:prstGeom'); + $objWriter->writeAttribute('prst', 'rect'); + // a:avLst + $objWriter->writeElement('a:avLst', null); + $objWriter->endElement(); + if ($shape->getBorder()->getLineStyle() != Border::LINE_NONE) { + $this->writeBorder($objWriter, $shape->getBorder(), ''); + } + if ($shape->getShadow()->isVisible()) { + $this->writeShadow($objWriter, $shape->getShadow()); + } + $objWriter->endElement(); + $objWriter->endElement(); + } - switch ($transition->getTransitionType()) { - case Transition::TRANSITION_BLINDS_HORIZONTAL: - $objWriter->startElement('p:blinds'); - $objWriter->writeAttribute('dir', 'horz'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_BLINDS_VERTICAL: - $objWriter->startElement('p:blinds'); - $objWriter->writeAttribute('dir', 'vert'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_CHECKER_HORIZONTAL: - $objWriter->startElement('p:checker'); - $objWriter->writeAttribute('dir', 'horz'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_CHECKER_VERTICAL: - $objWriter->startElement('p:checker'); - $objWriter->writeAttribute('dir', 'vert'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_CIRCLE_HORIZONTAL: - $objWriter->startElement('p:circle'); - $objWriter->writeAttribute('dir', 'horz'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_CIRCLE_VERTICAL: - $objWriter->startElement('p:circle'); - $objWriter->writeAttribute('dir', 'vert'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_COMB_HORIZONTAL: - $objWriter->startElement('p:comb'); - $objWriter->writeAttribute('dir', 'horz'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_COMB_VERTICAL: - $objWriter->startElement('p:comb'); - $objWriter->writeAttribute('dir', 'vert'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_COVER_DOWN: - $objWriter->startElement('p:cover'); - $objWriter->writeAttribute('dir', 'd'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_COVER_LEFT: - $objWriter->startElement('p:cover'); - $objWriter->writeAttribute('dir', 'l'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_COVER_LEFT_DOWN: - $objWriter->startElement('p:cover'); - $objWriter->writeAttribute('dir', 'ld'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_COVER_LEFT_UP: - $objWriter->startElement('p:cover'); - $objWriter->writeAttribute('dir', 'lu'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_COVER_RIGHT: - $objWriter->startElement('p:cover'); - $objWriter->writeAttribute('dir', 'r'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_COVER_RIGHT_DOWN: - $objWriter->startElement('p:cover'); - $objWriter->writeAttribute('dir', 'rd'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_COVER_RIGHT_UP: - $objWriter->startElement('p:cover'); - $objWriter->writeAttribute('dir', 'ru'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_COVER_UP: - $objWriter->startElement('p:cover'); - $objWriter->writeAttribute('dir', 'u'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_CUT: - $objWriter->writeElement('p:cut'); - break; - case Transition::TRANSITION_DIAMOND: - $objWriter->writeElement('p:diamond'); - break; - case Transition::TRANSITION_DISSOLVE: - $objWriter->writeElement('p:dissolve'); - break; - case Transition::TRANSITION_FADE: - $objWriter->writeElement('p:fade'); - break; - case Transition::TRANSITION_NEWSFLASH: - $objWriter->writeElement('p:newsflash'); - break; - case Transition::TRANSITION_PLUS: - $objWriter->writeElement('p:plus'); - break; - case Transition::TRANSITION_PULL_DOWN: - $objWriter->startElement('p:pull'); - $objWriter->writeAttribute('dir', 'd'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_PULL_LEFT: - $objWriter->startElement('p:pull'); - $objWriter->writeAttribute('dir', 'l'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_PULL_RIGHT: - $objWriter->startElement('p:pull'); - $objWriter->writeAttribute('dir', 'r'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_PULL_UP: - $objWriter->startElement('p:pull'); - $objWriter->writeAttribute('dir', 'u'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_PUSH_DOWN: - $objWriter->startElement('p:push'); - $objWriter->writeAttribute('dir', 'd'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_PUSH_LEFT: - $objWriter->startElement('p:push'); - $objWriter->writeAttribute('dir', 'l'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_PUSH_RIGHT: - $objWriter->startElement('p:push'); - $objWriter->writeAttribute('dir', 'r'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_PUSH_UP: - $objWriter->startElement('p:push'); - $objWriter->writeAttribute('dir', 'u'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_RANDOM: - $objWriter->writeElement('p:random'); - break; - case Transition::TRANSITION_RANDOMBAR_HORIZONTAL: - $objWriter->startElement('p:randomBar'); - $objWriter->writeAttribute('dir', 'horz'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_RANDOMBAR_VERTICAL: - $objWriter->startElement('p:randomBar'); - $objWriter->writeAttribute('dir', 'vert'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_SPLIT_IN_HORIZONTAL: - $objWriter->startElement('p:split'); - $objWriter->writeAttribute('dir', 'in'); - $objWriter->writeAttribute('orient', 'horz'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_SPLIT_OUT_HORIZONTAL: - $objWriter->startElement('p:split'); - $objWriter->writeAttribute('dir', 'out'); - $objWriter->writeAttribute('orient', 'horz'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_SPLIT_IN_VERTICAL: - $objWriter->startElement('p:split'); - $objWriter->writeAttribute('dir', 'in'); - $objWriter->writeAttribute('orient', 'vert'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_SPLIT_OUT_VERTICAL: - $objWriter->startElement('p:split'); - $objWriter->writeAttribute('dir', 'out'); - $objWriter->writeAttribute('orient', 'vert'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_STRIPS_LEFT_DOWN: - $objWriter->startElement('p:strips'); - $objWriter->writeAttribute('dir', 'ld'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_STRIPS_LEFT_UP: - $objWriter->startElement('p:strips'); - $objWriter->writeAttribute('dir', 'lu'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_STRIPS_RIGHT_DOWN: - $objWriter->startElement('p:strips'); - $objWriter->writeAttribute('dir', 'rd'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_STRIPS_RIGHT_UP: - $objWriter->startElement('p:strips'); - $objWriter->writeAttribute('dir', 'ru'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_WEDGE: - $objWriter->writeElement('p:wedge'); - break; - case Transition::TRANSITION_WIPE_DOWN: - $objWriter->startElement('p:wipe'); - $objWriter->writeAttribute('dir', 'd'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_WIPE_LEFT: - $objWriter->startElement('p:wipe'); - $objWriter->writeAttribute('dir', 'l'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_WIPE_RIGHT: - $objWriter->startElement('p:wipe'); - $objWriter->writeAttribute('dir', 'r'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_WIPE_UP: - $objWriter->startElement('p:wipe'); - $objWriter->writeAttribute('dir', 'u'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_ZOOM_IN: - $objWriter->startElement('p:zoom'); - $objWriter->writeAttribute('dir', 'in'); - $objWriter->endElement(); - break; - case Transition::TRANSITION_ZOOM_OUT: - $objWriter->startElement('p:zoom'); - $objWriter->writeAttribute('dir', 'out'); - $objWriter->endElement(); - break; + /** + * Write group + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Shape\Group $group + * @param int $shapeId + */ + protected function writeShapeGroup(XMLWriter $objWriter, Group $group, &$shapeId) + { + // p:grpSp + $objWriter->startElement('p:grpSp'); + // p:nvGrpSpPr + $objWriter->startElement('p:nvGrpSpPr'); + // p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('name', 'Group ' . $shapeId++); + $objWriter->writeAttribute('id', $shapeId); + $objWriter->endElement(); // p:cNvPr + // NOTE: Re: $shapeId This seems to be how PowerPoint 2010 does business. + // p:cNvGrpSpPr + $objWriter->writeElement('p:cNvGrpSpPr', null); + // p:nvPr + $objWriter->writeElement('p:nvPr', null); + $objWriter->endElement(); // p:nvGrpSpPr + // p:grpSpPr + $objWriter->startElement('p:grpSpPr'); + // a:xfrm + $objWriter->startElement('a:xfrm'); + // a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($group->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($group->getOffsetY())); + $objWriter->endElement(); // a:off + // a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($group->getExtentX())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($group->getExtentY())); + $objWriter->endElement(); // a:ext + // a:chOff + $objWriter->startElement('a:chOff'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($group->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($group->getOffsetY())); + $objWriter->endElement(); // a:chOff + // a:chExt + $objWriter->startElement('a:chExt'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($group->getExtentX())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($group->getExtentY())); + $objWriter->endElement(); // a:chExt + $objWriter->endElement(); // a:xfrm + $objWriter->endElement(); // p:grpSpPr + + $this->writeShapeCollection($objWriter, $group->getShapeCollection(), $shapeId); + + $objWriter->endElement(); // p:grpSp + } + + /** + * @param \PhpOffice\PhpPresentation\Slide\AbstractSlide $pSlide + * @param $objWriter + */ + protected function writeSlideBackground(AbstractSlideAlias $pSlide, XMLWriter $objWriter) + { + if (!($pSlide->getBackground() instanceof Slide\AbstractBackground)) { + return; } + $oBackground = $pSlide->getBackground(); + // p:bg + $objWriter->startElement('p:bg'); + if ($oBackground instanceof Slide\Background\Color) { + // p:bgPr + $objWriter->startElement('p:bgPr'); + // a:solidFill + $objWriter->startElement('a:solidFill'); + // a:srgbClr + $objWriter->startElement('a:srgbClr'); + $objWriter->writeAttribute('val', $oBackground->getColor()->getRGB()); + $objWriter->endElement(); + // > a:solidFill + $objWriter->endElement(); + + // p:bgPr/a:effectLst + $objWriter->writeElement('a:effectLst'); + // > p:bgPr + $objWriter->endElement(); + } + if ($oBackground instanceof Slide\Background\Image) { + // p:bgPr + $objWriter->startElement('p:bgPr'); + // a:blipFill + $objWriter->startElement('a:blipFill'); + // a:blip + $objWriter->startElement('a:blip'); + $objWriter->writeAttribute('r:embed', $oBackground->relationId); + // > a:blipFill + $objWriter->endElement(); + // a:stretch + $objWriter->startElement('a:stretch'); + // a:fillRect + $objWriter->writeElement('a:fillRect'); + // > a:stretch + $objWriter->endElement(); + // > a:blipFill + $objWriter->endElement(); + // > p:bgPr + $objWriter->endElement(); + } + /** + * @link : http://www.officeopenxml.com/prSlide-background.php + */ + if ($oBackground instanceof Slide\Background\SchemeColor) { + // p:bgRef + $objWriter->startElement('p:bgRef'); + $objWriter->writeAttribute('idx', '1001'); + // a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', $oBackground->getSchemeColor()->getValue()); + $objWriter->endElement(); + // > p:bgRef + $objWriter->endElement(); + } + // > p:bg $objWriter->endElement(); } + + private function getGUID() + { + if (function_exists('com_create_guid')) { + return com_create_guid(); + } else { + mt_srand((double)microtime() * 10000);//optional for php 4.2.0 and up. + $charid = strtoupper(md5(uniqid(rand(), true))); + $hyphen = chr(45);// "-" + $uuid = chr(123)// "{" + . substr($charid, 0, 8) . $hyphen + . substr($charid, 8, 4) . $hyphen + . substr($charid, 12, 4) . $hyphen + . substr($charid, 16, 4) . $hyphen + . substr($charid, 20, 12) + . chr(125);// "}" + return $uuid; + } + } } diff --git a/src/PhpPresentation/Writer/PowerPoint2007/CommentAuthors.php b/src/PhpPresentation/Writer/PowerPoint2007/CommentAuthors.php new file mode 100644 index 000000000..b730bd201 --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/CommentAuthors.php @@ -0,0 +1,75 @@ +getPresentation()->getAllSlides() as $oSlide) { + foreach ($oSlide->getShapeCollection() as $oShape) { + if (!($oShape instanceof Comment)) { + continue; + } + $oAuthor = $oShape->getAuthor(); + if (!($oAuthor instanceof Author)) { + continue; + } + if (array_key_exists($oAuthor->getHashCode(), $arrayAuthors)) { + continue; + } + $arrayAuthors[$oAuthor->getHashCode()] = $oAuthor; + } + } + if (!empty($arrayAuthors)) { + $this->getZip()->addFromString('ppt/commentAuthors.xml', $this->writeCommentsAuthors($arrayAuthors)); + } + + return $this->getZip(); + } + + /** + * @param Author[] $arrayAuthors + * @return string + */ + protected function writeCommentsAuthors($arrayAuthors) + { + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + + // p:cmAuthorLst + $objWriter->startElement('p:cmAuthorLst'); + $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main'); + + $idxAuthor = 0; + foreach ($arrayAuthors as $oAuthor) { + $oAuthor->setIndex($idxAuthor++); + + // p:cmAuthor + $objWriter->startElement('p:cmAuthor'); + $objWriter->writeAttribute('id', $oAuthor->getIndex()); + $objWriter->writeAttribute('name', $oAuthor->getName()); + $objWriter->writeAttribute('initials', $oAuthor->getInitials()); + $objWriter->writeAttribute('lastIdx', "2"); + $objWriter->writeAttribute('clrIdx', 0); + $objWriter->endElement(); + } + + // ## p:cmAuthorLst + $objWriter->endElement(); + + return $objWriter->getData(); + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/ContentTypes.php b/src/PhpPresentation/Writer/PowerPoint2007/ContentTypes.php index 431bc44d0..1a9e9f5cb 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/ContentTypes.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/ContentTypes.php @@ -17,34 +17,24 @@ namespace PhpOffice\PhpPresentation\Writer\PowerPoint2007; -use PhpOffice\PhpPresentation\PhpPresentation; use PhpOffice\PhpPresentation\Shape\Chart as ShapeChart; use PhpOffice\PhpPresentation\Shape\Drawing as ShapeDrawing; -use PhpOffice\PhpPresentation\Shape\MemoryDrawing; -use PhpOffice\Common\File; use PhpOffice\Common\XMLWriter; use PhpOffice\PhpPresentation\Writer\PowerPoint2007; /** * \PhpOffice\PhpPresentation\Writer\PowerPoint2007\ContentTypes */ -class ContentTypes extends AbstractPart +class ContentTypes extends AbstractDecoratorWriter { /** - * Write content types to XML format - * - * @param PhpPresentation $pPhpPresentation - * @return string XML Output + * @return \PhpOffice\Common\Adapter\Zip\ZipInterface * @throws \Exception */ - public function writeContentTypes(PhpPresentation $pPhpPresentation) + public function render() { - $parentWriter = $this->getParentWriter(); - if (!$parentWriter instanceof PowerPoint2007) { - throw new \Exception('The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\PowerPoint2007'); - } // Create XML writer - $objWriter = $this->getXMLWriter(); + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); // XML header $objWriter->startDocument('1.0', 'UTF-8', 'yes'); @@ -59,12 +49,6 @@ public function writeContentTypes(PhpPresentation $pPhpPresentation) // XML $this->writeDefaultContentType($objWriter, 'xml', 'application/xml'); - // Themes - $masterSlides = $parentWriter->getLayoutPack()->getMasterSlides(); - foreach ($masterSlides as $masterSlide) { - $this->writeOverrideContentType($objWriter, '/ppt/theme/theme' . $masterSlide['masterid'] . '.xml', 'application/vnd.openxmlformats-officedocument.theme+xml'); - } - // Presentation $this->writeOverrideContentType($objWriter, '/ppt/presentation.xml', 'application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml'); @@ -72,54 +56,35 @@ public function writeContentTypes(PhpPresentation $pPhpPresentation) $this->writeOverrideContentType($objWriter, '/ppt/presProps.xml', 'application/vnd.openxmlformats-officedocument.presentationml.presProps+xml'); $this->writeOverrideContentType($objWriter, '/ppt/tableStyles.xml', 'application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml'); $this->writeOverrideContentType($objWriter, '/ppt/viewProps.xml', 'application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml'); - + // DocProps $this->writeOverrideContentType($objWriter, '/docProps/app.xml', 'application/vnd.openxmlformats-officedocument.extended-properties+xml'); $this->writeOverrideContentType($objWriter, '/docProps/core.xml', 'application/vnd.openxmlformats-package.core-properties+xml'); $this->writeOverrideContentType($objWriter, '/docProps/custom.xml', 'application/vnd.openxmlformats-officedocument.custom-properties+xml'); - - // Slide masters - $masterSlides = $parentWriter->getLayoutPack()->getMasterSlides(); - foreach ($masterSlides as $masterSlide) { - $this->writeOverrideContentType($objWriter, '/ppt/slideMasters/slideMaster' . $masterSlide['masterid'] . '.xml', 'application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml'); - } - // Slide layouts - $slideLayouts = $parentWriter->getLayoutPack()->getLayouts(); - $numSlideLayouts = count($slideLayouts); - for ($i = 0; $i < $numSlideLayouts; ++$i) { - $this->writeOverrideContentType($objWriter, '/ppt/slideLayouts/slideLayout' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml'); + // Slide masters + $sldLayoutNr = 0; + $sldLayoutId = time() + 689016272; // requires minimum value of 2 147 483 648 + foreach ($this->oPresentation->getAllMasterSlides() as $idx => $oSlideMaster) { + $oSlideMaster->setRelsIndex($idx + 1); + $this->writeOverrideContentType($objWriter, '/ppt/slideMasters/slideMaster' . $oSlideMaster->getRelsIndex() . '.xml', 'application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml'); + $this->writeOverrideContentType($objWriter, '/ppt/theme/theme' . $oSlideMaster->getRelsIndex() . '.xml', 'application/vnd.openxmlformats-officedocument.theme+xml'); + foreach ($oSlideMaster->getAllSlideLayouts() as $oSlideLayout) { + $oSlideLayout->layoutNr = ++$sldLayoutNr; + $oSlideLayout->layoutId = ++$sldLayoutId; + $this->writeOverrideContentType($objWriter, '/ppt/slideLayouts/slideLayout' . $oSlideLayout->layoutNr . '.xml', 'application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml'); + } } // Slides - $slideCount = $pPhpPresentation->getSlideCount(); + $slideCount = $this->oPresentation->getSlideCount(); for ($i = 0; $i < $slideCount; ++$i) { $this->writeOverrideContentType($objWriter, '/ppt/slides/slide' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.presentationml.slide+xml'); - if ($pPhpPresentation->getSlide($i)->getNote()->getShapeCollection()->count() > 0) { + if ($this->oPresentation->getSlide($i)->getNote()->getShapeCollection()->count() > 0) { $this->writeOverrideContentType($objWriter, '/ppt/notesSlides/notesSlide' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml'); } } - // Add layoutpack content types - $otherRelations = $parentWriter->getLayoutPack()->getMasterSlideRelations(); - foreach ($otherRelations as $otherRelation) { - if (strpos($otherRelation['target'], 'http://') !== 0 && $otherRelation['contentType'] != '') { - $this->writeOverrideContentType($objWriter, '/ppt/slideMasters/' . $otherRelation['target'], $otherRelation['contentType']); - } - } - $otherRelations = $parentWriter->getLayoutPack()->getThemeRelations(); - foreach ($otherRelations as $otherRelation) { - if (strpos($otherRelation['target'], 'http://') !== 0 && $otherRelation['contentType'] != '') { - $this->writeOverrideContentType($objWriter, '/ppt/theme/' . $otherRelation['target'], $otherRelation['contentType']); - } - } - $otherRelations = $parentWriter->getLayoutPack()->getLayoutRelations(); - foreach ($otherRelations as $otherRelation) { - if (strpos($otherRelation['target'], 'http://') !== 0 && $otherRelation['contentType'] != '') { - $this->writeOverrideContentType($objWriter, '/ppt/slideLayouts/' . $otherRelation['target'], $otherRelation['contentType']); - } - } - // Add media content-types $aMediaContentTypes = array(); @@ -136,26 +101,15 @@ public function writeContentTypes(PhpPresentation $pPhpPresentation) $this->writeDefaultContentType($objWriter, 'xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); // Other media content types - $mediaCount = $parentWriter->getDrawingHashTable()->count(); + $mediaCount = $this->getDrawingHashTable()->count(); for ($i = 0; $i < $mediaCount; ++$i) { - $extension = ''; - $mimeType = ''; - - $shapeIndex = $parentWriter->getDrawingHashTable()->getByIndex($i); + $shapeIndex = $this->getDrawingHashTable()->getByIndex($i); if ($shapeIndex instanceof ShapeChart) { // Chart content type $this->writeOverrideContentType($objWriter, '/ppt/charts/chart' . $shapeIndex->getImageIndex() . '.xml', 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml'); } else { - if ($shapeIndex instanceof ShapeDrawing) { - $extension = strtolower($shapeIndex->getExtension()); - $mimeType = $this->getImageMimeType($shapeIndex->getPath()); - } elseif ($shapeIndex instanceof MemoryDrawing) { - $extension = strtolower($shapeIndex->getMimeType()); - $extension = explode('/', $extension); - $extension = $extension[1]; - - $mimeType = $shapeIndex->getMimeType(); - } + $extension = strtolower($shapeIndex->getExtension()); + $mimeType = $shapeIndex->getMimeType(); if (!isset($aMediaContentTypes[$extension])) { $aMediaContentTypes[$extension] = $mimeType; @@ -167,35 +121,9 @@ public function writeContentTypes(PhpPresentation $pPhpPresentation) $objWriter->endElement(); - // Return - return $objWriter->getData(); - } - - /** - * Get image mime type - * - * @param string $pFile Filename - * @return string Mime Type - * @throws \Exception - */ - private function getImageMimeType($pFile = '') - { - if (File::fileExists($pFile)) { - if (strpos($pFile, 'zip://') === 0) { - $pZIPFile = str_replace('zip://', '', $pFile); - $pZIPFile = substr($pZIPFile, 0, strpos($pZIPFile, '#')); - $pImgFile = substr($pFile, strpos($pFile, '#') + 1); - $oArchive = new \ZipArchive(); - $oArchive->open($pZIPFile); - $image = getimagesizefromstring($oArchive->getFromName($pImgFile)); - } else { - $image = getimagesize($pFile); - } + $this->oZip->addFromString('[Content_Types].xml', $objWriter->getData()); - return image_type_to_mime_type($image[2]); - } else { - throw new \Exception("File $pFile does not exist"); - } + return $this->oZip; } /** @@ -208,15 +136,14 @@ private function getImageMimeType($pFile = '') */ private function writeDefaultContentType(XMLWriter $objWriter, $pPartname = '', $pContentType = '') { - if ($pPartname != '' && $pContentType != '') { - // Write content type - $objWriter->startElement('Default'); - $objWriter->writeAttribute('Extension', $pPartname); - $objWriter->writeAttribute('ContentType', $pContentType); - $objWriter->endElement(); - } else { + if ($pPartname == '' || $pContentType == '') { throw new \Exception("Invalid parameters passed."); } + // Write content type + $objWriter->startElement('Default'); + $objWriter->writeAttribute('Extension', $pPartname); + $objWriter->writeAttribute('ContentType', $pContentType); + $objWriter->endElement(); } /** @@ -229,14 +156,13 @@ private function writeDefaultContentType(XMLWriter $objWriter, $pPartname = '', */ private function writeOverrideContentType(XMLWriter $objWriter, $pPartname = '', $pContentType = '') { - if ($pPartname != '' && $pContentType != '') { - // Write content type - $objWriter->startElement('Override'); - $objWriter->writeAttribute('PartName', $pPartname); - $objWriter->writeAttribute('ContentType', $pContentType); - $objWriter->endElement(); - } else { + if ($pPartname == '' || $pContentType == '') { throw new \Exception("Invalid parameters passed."); } + // Write content type + $objWriter->startElement('Override'); + $objWriter->writeAttribute('PartName', $pPartname); + $objWriter->writeAttribute('ContentType', $pContentType); + $objWriter->endElement(); } } diff --git a/src/PhpPresentation/Writer/PowerPoint2007/DocProps.php b/src/PhpPresentation/Writer/PowerPoint2007/DocProps.php deleted file mode 100644 index 8554fc12d..000000000 --- a/src/PhpPresentation/Writer/PowerPoint2007/DocProps.php +++ /dev/null @@ -1,232 +0,0 @@ -getXMLWriter(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // Properties - $objWriter->startElement('Properties'); - $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties'); - $objWriter->writeAttribute('xmlns:vt', 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes'); - - // Application - $objWriter->writeElement('Application', 'Microsoft Office PowerPoint'); - - // Slides - $objWriter->writeElement('Slides', $pPhpPresentation->getSlideCount()); - - // ScaleCrop - $objWriter->writeElement('ScaleCrop', 'false'); - - // HeadingPairs - $objWriter->startElement('HeadingPairs'); - - // Vector - $objWriter->startElement('vt:vector'); - $objWriter->writeAttribute('size', '4'); - $objWriter->writeAttribute('baseType', 'variant'); - - // Variant - $objWriter->startElement('vt:variant'); - $objWriter->writeElement('vt:lpstr', 'Theme'); - $objWriter->endElement(); - - // Variant - $objWriter->startElement('vt:variant'); - $objWriter->writeElement('vt:i4', '1'); - $objWriter->endElement(); - - // Variant - $objWriter->startElement('vt:variant'); - $objWriter->writeElement('vt:lpstr', 'Slide Titles'); - $objWriter->endElement(); - - // Variant - $objWriter->startElement('vt:variant'); - $objWriter->writeElement('vt:i4', '1'); - $objWriter->endElement(); - - $objWriter->endElement(); - - $objWriter->endElement(); - - // TitlesOfParts - $objWriter->startElement('TitlesOfParts'); - - // Vector - $objWriter->startElement('vt:vector'); - $objWriter->writeAttribute('size', '1'); - $objWriter->writeAttribute('baseType', 'lpstr'); - - $objWriter->writeElement('vt:lpstr', 'Office Theme'); - - $objWriter->endElement(); - - $objWriter->endElement(); - - // Company - $objWriter->writeElement('Company', $pPhpPresentation->getProperties()->getCompany()); - - // LinksUpToDate - $objWriter->writeElement('LinksUpToDate', 'false'); - - // SharedDoc - $objWriter->writeElement('SharedDoc', 'false'); - - // HyperlinksChanged - $objWriter->writeElement('HyperlinksChanged', 'false'); - - // AppVersion - $objWriter->writeElement('AppVersion', '12.0000'); - - $objWriter->endElement(); - - // Return - return $objWriter->getData(); - } - - /** - * Write docProps/core.xml to XML format - * - * @param PhpPresentation $pPhpPresentation - * @return string XML Output - * @throws \Exception - */ - public function writeDocPropsCore(PhpPresentation $pPhpPresentation) - { - // Create XML writer - $objWriter = $this->getXMLWriter(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // cp:coreProperties - $objWriter->startElement('cp:coreProperties'); - $objWriter->writeAttribute('xmlns:cp', 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties'); - $objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/'); - $objWriter->writeAttribute('xmlns:dcterms', 'http://purl.org/dc/terms/'); - $objWriter->writeAttribute('xmlns:dcmitype', 'http://purl.org/dc/dcmitype/'); - $objWriter->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); - - // dc:creator - $objWriter->writeElement('dc:creator', $pPhpPresentation->getProperties()->getCreator()); - - // cp:lastModifiedBy - $objWriter->writeElement('cp:lastModifiedBy', $pPhpPresentation->getProperties()->getLastModifiedBy()); - - // dcterms:created - $objWriter->startElement('dcterms:created'); - $objWriter->writeAttribute('xsi:type', 'dcterms:W3CDTF'); - $objWriter->writeRaw(gmdate("Y-m-d\TH:i:s\Z", $pPhpPresentation->getProperties()->getCreated())); - $objWriter->endElement(); - - // dcterms:modified - $objWriter->startElement('dcterms:modified'); - $objWriter->writeAttribute('xsi:type', 'dcterms:W3CDTF'); - $objWriter->writeRaw(gmdate("Y-m-d\TH:i:s\Z", $pPhpPresentation->getProperties()->getModified())); - $objWriter->endElement(); - - // dc:title - $objWriter->writeElement('dc:title', $pPhpPresentation->getProperties()->getTitle()); - - // dc:description - $objWriter->writeElement('dc:description', $pPhpPresentation->getProperties()->getDescription()); - - // dc:subject - $objWriter->writeElement('dc:subject', $pPhpPresentation->getProperties()->getSubject()); - - // cp:keywords - $objWriter->writeElement('cp:keywords', $pPhpPresentation->getProperties()->getKeywords()); - - // cp:category - $objWriter->writeElement('cp:category', $pPhpPresentation->getProperties()->getCategory()); - - if ($pPhpPresentation->isMarkedAsFinal()) { - // cp:contentStatus = Final - $objWriter->writeElement('cp:contentStatus', 'Final'); - } - - $objWriter->endElement(); - - // Return - return $objWriter->getData(); - } - - - - /** - * Write docProps/custom.xml to XML format - * - * @param PhpPresentation $pPhpPresentation - * @return string XML Output - * @throws \Exception - */ - public function writeDocPropsCustom(PhpPresentation $pPhpPresentation) - { - // Create XML writer - $objWriter = $this->getXMLWriter(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // Properties - $objWriter->startElement('Properties'); - $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/officeDocument/2006/custom-properties'); - $objWriter->writeAttribute('xmlns:vt', 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes'); - - if ($pPhpPresentation->isMarkedAsFinal()) { - // property - $objWriter->startElement('property'); - $objWriter->writeAttribute('fmtid', '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}'); - $objWriter->writeAttribute('pid', 2); - $objWriter->writeAttribute('name', '_MarkAsFinal'); - - // property > vt:bool - $objWriter->writeElement('vt:bool', 'true'); - - // > property - $objWriter->endElement(); - } - - // > Properties - $objWriter->endElement(); - - // Return - return $objWriter->getData(); - } -} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/DocPropsApp.php b/src/PhpPresentation/Writer/PowerPoint2007/DocPropsApp.php new file mode 100644 index 000000000..c5a301098 --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/DocPropsApp.php @@ -0,0 +1,102 @@ +startDocument('1.0', 'UTF-8', 'yes'); + + // Properties + $objWriter->startElement('Properties'); + $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties'); + $objWriter->writeAttribute('xmlns:vt', 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes'); + + // Application + $objWriter->writeElement('Application', 'Microsoft Office PowerPoint'); + + // Slides + $objWriter->writeElement('Slides', $this->getPresentation()->getSlideCount()); + + // ScaleCrop + $objWriter->writeElement('ScaleCrop', 'false'); + + // HeadingPairs + $objWriter->startElement('HeadingPairs'); + + // Vector + $objWriter->startElement('vt:vector'); + $objWriter->writeAttribute('size', '4'); + $objWriter->writeAttribute('baseType', 'variant'); + + // Variant + $objWriter->startElement('vt:variant'); + $objWriter->writeElement('vt:lpstr', 'Theme'); + $objWriter->endElement(); + + // Variant + $objWriter->startElement('vt:variant'); + $objWriter->writeElement('vt:i4', '1'); + $objWriter->endElement(); + + // Variant + $objWriter->startElement('vt:variant'); + $objWriter->writeElement('vt:lpstr', 'Slide Titles'); + $objWriter->endElement(); + + // Variant + $objWriter->startElement('vt:variant'); + $objWriter->writeElement('vt:i4', '1'); + $objWriter->endElement(); + + $objWriter->endElement(); + + $objWriter->endElement(); + + // TitlesOfParts + $objWriter->startElement('TitlesOfParts'); + + // Vector + $objWriter->startElement('vt:vector'); + $objWriter->writeAttribute('size', '1'); + $objWriter->writeAttribute('baseType', 'lpstr'); + + $objWriter->writeElement('vt:lpstr', 'Office Theme'); + + $objWriter->endElement(); + + $objWriter->endElement(); + + // Company + $objWriter->writeElement('Company', $this->getPresentation()->getDocumentProperties()->getCompany()); + + // LinksUpToDate + $objWriter->writeElement('LinksUpToDate', 'false'); + + // SharedDoc + $objWriter->writeElement('SharedDoc', 'false'); + + // HyperlinksChanged + $objWriter->writeElement('HyperlinksChanged', 'false'); + + // AppVersion + $objWriter->writeElement('AppVersion', '12.0000'); + + $objWriter->endElement(); + + $this->oZip->addFromString('docProps/app.xml', $objWriter->getData()); + + // Return + return $this->oZip; + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/DocPropsCore.php b/src/PhpPresentation/Writer/PowerPoint2007/DocPropsCore.php new file mode 100644 index 000000000..dd954246a --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/DocPropsCore.php @@ -0,0 +1,73 @@ +startDocument('1.0', 'UTF-8', 'yes'); + + // cp:coreProperties + $objWriter->startElement('cp:coreProperties'); + $objWriter->writeAttribute('xmlns:cp', 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties'); + $objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/'); + $objWriter->writeAttribute('xmlns:dcterms', 'http://purl.org/dc/terms/'); + $objWriter->writeAttribute('xmlns:dcmitype', 'http://purl.org/dc/dcmitype/'); + $objWriter->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + + // dc:creator + $objWriter->writeElement('dc:creator', $this->oPresentation->getDocumentProperties()->getCreator()); + + // cp:lastModifiedBy + $objWriter->writeElement('cp:lastModifiedBy', $this->oPresentation->getDocumentProperties()->getLastModifiedBy()); + + // dcterms:created + $objWriter->startElement('dcterms:created'); + $objWriter->writeAttribute('xsi:type', 'dcterms:W3CDTF'); + $objWriter->writeRaw(gmdate("Y-m-d\TH:i:s\Z", $this->oPresentation->getDocumentProperties()->getCreated())); + $objWriter->endElement(); + + // dcterms:modified + $objWriter->startElement('dcterms:modified'); + $objWriter->writeAttribute('xsi:type', 'dcterms:W3CDTF'); + $objWriter->writeRaw(gmdate("Y-m-d\TH:i:s\Z", $this->oPresentation->getDocumentProperties()->getModified())); + $objWriter->endElement(); + + // dc:title + $objWriter->writeElement('dc:title', $this->oPresentation->getDocumentProperties()->getTitle()); + + // dc:description + $objWriter->writeElement('dc:description', $this->oPresentation->getDocumentProperties()->getDescription()); + + // dc:subject + $objWriter->writeElement('dc:subject', $this->oPresentation->getDocumentProperties()->getSubject()); + + // cp:keywords + $objWriter->writeElement('cp:keywords', $this->oPresentation->getDocumentProperties()->getKeywords()); + + // cp:category + $objWriter->writeElement('cp:category', $this->oPresentation->getDocumentProperties()->getCategory()); + + if ($this->oPresentation->getPresentationProperties()->isMarkedAsFinal()) { + // cp:contentStatus = Final + $objWriter->writeElement('cp:contentStatus', 'Final'); + } + + $objWriter->endElement(); + + $this->oZip->addFromString('docProps/core.xml', $objWriter->getData()); + + // Return + return $this->oZip; + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/DocPropsCustom.php b/src/PhpPresentation/Writer/PowerPoint2007/DocPropsCustom.php new file mode 100644 index 000000000..7d49a6374 --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/DocPropsCustom.php @@ -0,0 +1,47 @@ +startDocument('1.0', 'UTF-8', 'yes'); + + // Properties + $objWriter->startElement('Properties'); + $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/officeDocument/2006/custom-properties'); + $objWriter->writeAttribute('xmlns:vt', 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes'); + + if ($this->getPresentation()->getPresentationProperties()->isMarkedAsFinal()) { + // property + $objWriter->startElement('property'); + $objWriter->writeAttribute('fmtid', '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}'); + $objWriter->writeAttribute('pid', 2); + $objWriter->writeAttribute('name', '_MarkAsFinal'); + + // property > vt:bool + $objWriter->writeElement('vt:bool', 'true'); + + // > property + $objWriter->endElement(); + } + + // > Properties + $objWriter->endElement(); + + $this->oZip->addFromString('docProps/custom.xml', $objWriter->getData()); + + // Return + return $this->oZip; + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/DocPropsThumbnail.php b/src/PhpPresentation/Writer/PowerPoint2007/DocPropsThumbnail.php new file mode 100644 index 000000000..edd79caa7 --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/DocPropsThumbnail.php @@ -0,0 +1,31 @@ +getPresentation()->getPresentationProperties()->getThumbnailPath(); + + if ($pathThumbnail) { + $fileThumbnail = file_get_contents($pathThumbnail); + $gdImage = imagecreatefromstring($fileThumbnail); + if ($gdImage) { + ob_start(); + imagejpeg($gdImage); + $imageContents = ob_get_contents(); + ob_end_clean(); + imagedestroy($gdImage); + + $this->getZip()->addFromString('docProps/thumbnail.jpeg', $imageContents); + } + } + + // Return + return $this->getZip(); + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/Drawing.php b/src/PhpPresentation/Writer/PowerPoint2007/Drawing.php deleted file mode 100644 index 6e628b85d..000000000 --- a/src/PhpPresentation/Writer/PowerPoint2007/Drawing.php +++ /dev/null @@ -1,68 +0,0 @@ -getSlideCount(); - for ($i = 0; $i < $slideCount; ++$i) { - // Loop trough images and add to array - if ($pPhpPresentation->getSlide($i)->getShapeCollection()->count() > 0) { - $iterator = $pPhpPresentation->getSlide($i)->getShapeCollection()->getIterator(); - while ($iterator->valid()) { - if ($iterator->current() instanceof AbstractDrawing && !($iterator->current() instanceof Table)) { - $aDrawings[] = $iterator->current(); - } elseif ($iterator->current() instanceof Group) { - $iterator2 = $iterator->current()->getShapeCollection()->getIterator(); - while ($iterator2->valid()) { - if ($iterator2->current() instanceof AbstractDrawing && !($iterator2->current() instanceof Table)) { - $aDrawings[] = $iterator2->current(); - } - $iterator2->next(); - } - } - - $iterator->next(); - } - } - } - - return $aDrawings; - } -} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/AbstractLayoutPack.php b/src/PhpPresentation/Writer/PowerPoint2007/LayoutPack/AbstractLayoutPack.php similarity index 93% rename from src/PhpPresentation/Writer/PowerPoint2007/AbstractLayoutPack.php rename to src/PhpPresentation/Writer/PowerPoint2007/LayoutPack/AbstractLayoutPack.php index 93345b07b..e957047b1 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/AbstractLayoutPack.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/LayoutPack/AbstractLayoutPack.php @@ -15,10 +15,11 @@ * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ -namespace PhpOffice\PhpPresentation\Writer\PowerPoint2007; +namespace PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack; /** * \PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack + * @deprecated 0.7 */ abstract class AbstractLayoutPack { @@ -194,10 +195,10 @@ public function findLayout($name = '', $masterId = 1) * @return int * @throws \Exception */ - public function findLayoutId($name = '', $masterId = 1) + public function findLayoutId($name = '') { foreach ($this->layouts as $layoutId => $layout) { - if ($layout['name'] == $name && $layout['masterid'] == $masterId) { + if ($layout['name'] == $name) { return $layoutId; } } @@ -213,10 +214,10 @@ public function findLayoutId($name = '', $masterId = 1) * @return int * @throws \Exception */ - public function findLayoutName($idLayout = '', $masterId = 1) + public function findLayoutName($idLayout = '') { foreach ($this->layouts as $layoutId => $layout) { - if ($layoutId == $idLayout && $layout['masterid'] == $masterId) { + if ($layoutId == $idLayout) { return $layout['name']; } } diff --git a/src/PhpPresentation/Writer/PowerPoint2007/LayoutPack/PackDefault.php b/src/PhpPresentation/Writer/PowerPoint2007/LayoutPack/PackDefault.php index 1929eaf70..d1bd3b8ed 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/LayoutPack/PackDefault.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/LayoutPack/PackDefault.php @@ -18,10 +18,10 @@ namespace PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack; use PhpOffice\PhpPresentation\Slide\Layout; -use PhpOffice\PhpPresentation\Writer\PowerPoint2007\AbstractLayoutPack; /** * \PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack\Default + * @deprecated 0.7 */ class PackDefault extends AbstractLayoutPack { diff --git a/src/PhpPresentation/Writer/PowerPoint2007/LayoutPack/TemplateBased.php b/src/PhpPresentation/Writer/PowerPoint2007/LayoutPack/TemplateBased.php index e4b4a5662..7a42c4cc5 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/LayoutPack/TemplateBased.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/LayoutPack/TemplateBased.php @@ -17,10 +17,9 @@ namespace PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack; -use PhpOffice\PhpPresentation\Writer\PowerPoint2007\AbstractLayoutPack; - /** * \PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack\TemplateBased + * @deprecated 0.7 */ class TemplateBased extends AbstractLayoutPack { diff --git a/src/PhpPresentation/Writer/PowerPoint2007/Chart.php b/src/PhpPresentation/Writer/PowerPoint2007/PptCharts.php similarity index 81% rename from src/PhpPresentation/Writer/PowerPoint2007/Chart.php rename to src/PhpPresentation/Writer/PowerPoint2007/PptCharts.php index d94a18516..c8b9ee0e6 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/Chart.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/PptCharts.php @@ -1,27 +1,15 @@ getDrawingHashTable()->count(); ++$i) { + $shape = $this->getDrawingHashTable()->getByIndex($i); + if ($shape instanceof Chart) { + $this->getZip()->addFromString('ppt/charts/' . $shape->getIndexedFilename(), $this->writeChart($shape)); + + if ($shape->hasIncludedSpreadsheet()) { + $this->getZip()->addFromString('ppt/charts/_rels/' . $shape->getIndexedFilename() . '.rels', $this->writeChartRelationships($shape)); + $pFilename = 'PHPExcel'; + $this->getZip()->addFromString('ppt/embeddings/' . $shape->getIndexedFilename() . '.xlsx', $this->writeSpreadsheet($this->getPresentation(), $shape, $pFilename . '.xlsx')); + } + } + } + return $this->getZip(); + } + + /** * Write chart to XML format * @@ -47,15 +51,10 @@ class Chart extends Slide * @return string XML Output * @throws \Exception */ - public function writeChart(ShapeChart $chart = null) + public function writeChart(Chart $chart) { - // Check slide - if (is_null($chart)) { - throw new \Exception("Invalid \PhpOffice\PhpPresentation\Shape\Chart object passed."); - } - // Create XML writer - $objWriter = $this->getXMLWriter(); + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); // XML header $objWriter->startDocument('1.0', 'UTF-8', 'yes'); @@ -165,16 +164,7 @@ public function writeChart(ShapeChart $chart = null) $objWriter->writeAttribute('algn', $chart->getShadow()->getAlignment()); $objWriter->writeAttribute('rotWithShape', '0'); - // a:srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $chart->getShadow()->getColor()->getRGB()); - - // a:alpha - $objWriter->startElement('a:alpha'); - $objWriter->writeAttribute('val', $chart->getShadow()->getAlpha() * 1000); - $objWriter->endElement(); - - $objWriter->endElement(); + $this->writeColor($objWriter, $chart->getShadow()->getColor(), $chart->getShadow()->getAlpha()); $objWriter->endElement(); @@ -232,7 +222,7 @@ public function writeSpreadsheet(PhpPresentation $presentation, $chart, $tempNam if (strlen($title) == 0) { $title = 'Chart'; } - $workbook->getProperties()->setCreator($presentation->getProperties()->getCreator())->setLastModifiedBy($presentation->getProperties()->getLastModifiedBy())->setTitle($title); + $workbook->getProperties()->setCreator($presentation->getDocumentProperties()->getCreator())->setLastModifiedBy($presentation->getDocumentProperties()->getLastModifiedBy())->setTitle($title); // Add chart data $sheet = $workbook->setActiveSheetIndex(0); @@ -240,7 +230,7 @@ public function writeSpreadsheet(PhpPresentation $presentation, $chart, $tempNam // Write series $seriesIndex = 0; - foreach ($chart->getPlotArea()->getType()->getData() as $series) { + foreach ($chart->getPlotArea()->getType()->getSeries() as $series) { // Title $sheet->setCellValueByColumnAndRow(1 + $seriesIndex, 1, $series->getTitle()); @@ -301,22 +291,23 @@ protected function writeSingleValueOrReference($objWriter, $isReference, $value, if (!$isReference) { // Value $objWriter->writeElement('c:v', $value); - } else { - // Reference and cache - $objWriter->startElement('c:strRef'); - $objWriter->writeElement('c:f', $reference); - $objWriter->startElement('c:strCache'); - $objWriter->startElement('c:ptCount'); - $objWriter->writeAttribute('val', '1'); - $objWriter->endElement(); - - $objWriter->startElement('c:pt'); - $objWriter->writeAttribute('idx', '0'); - $objWriter->writeElement('c:v', $value); - $objWriter->endElement(); - $objWriter->endElement(); - $objWriter->endElement(); + return; } + + // Reference and cache + $objWriter->startElement('c:strRef'); + $objWriter->writeElement('c:f', $reference); + $objWriter->startElement('c:strCache'); + $objWriter->startElement('c:ptCount'); + $objWriter->writeAttribute('val', '1'); + $objWriter->endElement(); + + $objWriter->startElement('c:pt'); + $objWriter->writeAttribute('idx', '0'); + $objWriter->writeElement('c:v', $value); + $objWriter->endElement(); + $objWriter->endElement(); + $objWriter->endElement(); } /** @@ -332,10 +323,9 @@ protected function writeMultipleValuesOrReference($objWriter, $isReference, $val // c:strLit / c:numLit // c:strRef / c:numRef $referenceType = ($isReference ? 'Ref' : 'Lit'); + $dataType = 'str'; if (is_int($values[0]) || is_float($values[0])) { $dataType = 'num'; - } else { - $dataType = 'str'; } $objWriter->startElement('c:' . $dataType . $referenceType); @@ -435,22 +425,13 @@ protected function writeTitle(XMLWriter $objWriter, Title $subject) $objWriter->writeAttribute('strike', ($subject->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); $objWriter->writeAttribute('sz', ($subject->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $subject->getFont()->getUnderline()); - - if ($subject->getFont()->isSuperScript() || $subject->getFont()->isSubScript()) { - if ($subject->getFont()->isSuperScript()) { - $objWriter->writeAttribute('baseline', '30000'); - } elseif ($subject->getFont()->isSubScript()) { - $objWriter->writeAttribute('baseline', '-25000'); - } - } + $objWriter->writeAttributeIf($subject->getFont()->isSuperScript(), 'baseline', '30000'); + $objWriter->writeAttributeIf($subject->getFont()->isSubScript(), 'baseline', '-25000'); // Font - a:solidFill $objWriter->startElement('a:solidFill'); - // a:srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $subject->getFont()->getColor()->getRGB()); - $objWriter->endElement(); + $this->writeColor($objWriter, $subject->getFont()->getColor()); $objWriter->endElement(); @@ -497,7 +478,7 @@ protected function writeTitle(XMLWriter $objWriter, Title $subject) * @param \PhpOffice\PhpPresentation\Shape\Chart $chart * @throws \Exception */ - protected function writePlotArea(XMLWriter $objWriter, PlotArea $subject, ShapeChart $chart) + protected function writePlotArea(XMLWriter $objWriter, PlotArea $subject, Chart $chart) { // c:plotArea $objWriter->startElement('c:plotArea'); @@ -527,218 +508,12 @@ protected function writePlotArea(XMLWriter $objWriter, PlotArea $subject, ShapeC // Write X axis? if ($chartType->hasAxisX()) { - // c:catAx (Axis X) - $objWriter->startElement('c:catAx'); - - // c:axId - $objWriter->startElement('c:axId'); - $objWriter->writeAttribute('val', '52743552'); - $objWriter->endElement(); - - // c:scaling - $objWriter->startElement('c:scaling'); - - // c:orientation - $objWriter->startElement('c:orientation'); - $objWriter->writeAttribute('val', 'minMax'); - $objWriter->endElement(); - - $objWriter->endElement(); - - // c:axPos - $objWriter->startElement('c:axPos'); - $objWriter->writeAttribute('val', 'b'); - $objWriter->endElement(); - - // c:numFmt - $objWriter->startElement('c:numFmt'); - $objWriter->writeAttribute('formatCode', $subject->getAxisX()->getFormatCode()); - $objWriter->writeAttribute('sourceLinked', '0'); - $objWriter->endElement(); - - // c:majorTickMark - $objWriter->startElement('c:majorTickMark'); - $objWriter->writeAttribute('val', 'none'); - $objWriter->endElement(); - - // c:tickLblPos - $objWriter->startElement('c:tickLblPos'); - $objWriter->writeAttribute('val', 'nextTo'); - $objWriter->endElement(); - - // c:txPr - $objWriter->startElement('c:txPr'); - - // a:bodyPr - $objWriter->writeElement('a:bodyPr', null); - - // a:lstStyle - $objWriter->writeElement('a:lstStyle', null); - - // a:p - $objWriter->startElement('a:p'); - - // a:pPr - $objWriter->startElement('a:pPr'); - - // a:defRPr - $objWriter->writeElement('a:defRPr', null); - - $objWriter->endElement(); - - // a:r - $objWriter->startElement('a:r'); - - // a:rPr - $objWriter->startElement('a:rPr'); - $objWriter->writeAttribute('lang', 'en-US'); - $objWriter->writeAttribute('dirty', '0'); - $objWriter->endElement(); - - // a:t - $objWriter->writeElement('a:t', $subject->getAxisX()->getTitle()); - - $objWriter->endElement(); - - // a:endParaRPr - $objWriter->startElement('a:endParaRPr'); - $objWriter->writeAttribute('lang', 'en-US'); - $objWriter->writeAttribute('dirty', '0'); - $objWriter->endElement(); - - $objWriter->endElement(); - - $objWriter->endElement(); - - // c:crossAx - $objWriter->startElement('c:crossAx'); - $objWriter->writeAttribute('val', '52749440'); - $objWriter->endElement(); - - // c:crosses - $objWriter->startElement('c:crosses'); - $objWriter->writeAttribute('val', 'autoZero'); - $objWriter->endElement(); - - // c:lblAlgn - $objWriter->startElement('c:lblAlgn'); - $objWriter->writeAttribute('val', 'ctr'); - $objWriter->endElement(); - - // c:lblOffset - $objWriter->startElement('c:lblOffset'); - $objWriter->writeAttribute('val', '100'); - $objWriter->endElement(); - - $objWriter->endElement(); + $this->writeAxis($objWriter, $subject->getAxisX(), Chart\Axis::AXIS_X); } // Write Y axis? if ($chartType->hasAxisY()) { - // c:valAx (Axis Y) - $objWriter->startElement('c:valAx'); - - // c:axId - $objWriter->startElement('c:axId'); - $objWriter->writeAttribute('val', '52749440'); - $objWriter->endElement(); - - // c:scaling - $objWriter->startElement('c:scaling'); - - // c:orientation - $objWriter->startElement('c:orientation'); - $objWriter->writeAttribute('val', 'minMax'); - $objWriter->endElement(); - - // ## c:scaling - $objWriter->endElement(); - - // c:axPos - $objWriter->startElement('c:axPos'); - $objWriter->writeAttribute('val', 'l'); - $objWriter->endElement(); - - // c:numFmt - $objWriter->startElement('c:numFmt'); - $objWriter->writeAttribute('formatCode', $subject->getAxisY()->getFormatCode()); - $objWriter->writeAttribute('sourceLinked', '0'); - $objWriter->endElement(); - - // c:majorTickMark - $objWriter->startElement('c:majorTickMark'); - $objWriter->writeAttribute('val', 'none'); - $objWriter->endElement(); - - // c:tickLblPos - $objWriter->startElement('c:tickLblPos'); - $objWriter->writeAttribute('val', 'nextTo'); - $objWriter->endElement(); - - // c:txPr - $objWriter->startElement('c:txPr'); - - // a:bodyPr - $objWriter->writeElement('a:bodyPr', null); - - // a:lstStyle - $objWriter->writeElement('a:lstStyle', null); - - // a:p - $objWriter->startElement('a:p'); - - // a:pPr - $objWriter->startElement('a:pPr'); - - // a:defRPr - $objWriter->writeElement('a:defRPr', null); - - // ## a:pPr - $objWriter->endElement(); - - // a:r - $objWriter->startElement('a:r'); - - // a:rPr - $objWriter->startElement('a:rPr'); - $objWriter->writeAttribute('lang', 'en-US'); - $objWriter->writeAttribute('dirty', '0'); - $objWriter->endElement(); - - // a:t - $objWriter->writeElement('a:t', $subject->getAxisY()->getTitle()); - - // ## a:r - $objWriter->endElement(); - - // a:endParaRPr - $objWriter->startElement('a:endParaRPr'); - $objWriter->writeAttribute('lang', 'en-US'); - $objWriter->writeAttribute('dirty', '0'); - $objWriter->endElement(); - - // ## a:p - $objWriter->endElement(); - - // ## c:txPr - $objWriter->endElement(); - - // c:crossAx - $objWriter->startElement('c:crossAx'); - $objWriter->writeAttribute('val', '52743552'); - $objWriter->endElement(); - - // c:crosses - $objWriter->startElement('c:crosses'); - $objWriter->writeAttribute('val', 'autoZero'); - $objWriter->endElement(); - - // c:crossBetween - $objWriter->startElement('c:crossBetween'); - $objWriter->writeAttribute('val', 'between'); - $objWriter->endElement(); - - $objWriter->endElement(); + $this->writeAxis($objWriter, $subject->getAxisY(), Chart\Axis::AXIS_Y); } $objWriter->endElement(); @@ -811,22 +586,13 @@ protected function writeLegend(XMLWriter $objWriter, Legend $subject) $objWriter->writeAttribute('strike', ($subject->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); $objWriter->writeAttribute('sz', ($subject->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $subject->getFont()->getUnderline()); - - if ($subject->getFont()->isSuperScript() || $subject->getFont()->isSubScript()) { - if ($subject->getFont()->isSuperScript()) { - $objWriter->writeAttribute('baseline', '30000'); - } elseif ($subject->getFont()->isSubScript()) { - $objWriter->writeAttribute('baseline', '-25000'); - } - } + $objWriter->writeAttributeIf($subject->getFont()->isSuperScript(), 'baseline', '30000'); + $objWriter->writeAttributeIf($subject->getFont()->isSubScript(), 'baseline', '-25000'); // Font - a:solidFill $objWriter->startElement('a:solidFill'); - // a:srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $subject->getFont()->getColor()->getRGB()); - $objWriter->endElement(); + $this->writeColor($objWriter, $subject->getFont()->getColor()); $objWriter->endElement(); @@ -909,7 +675,7 @@ protected function writeLayout(XMLWriter $objWriter, $subject) } /** - * Write Type Line + * Write Type Area * * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer * @param \PhpOffice\PhpPresentation\Shape\Chart\Type\Area $subject @@ -928,45 +694,45 @@ protected function writeTypeArea(XMLWriter $objWriter, Area $subject, $includeSh // Write series $seriesIndex = 0; - foreach ($subject->getData() as $series) { + foreach ($subject->getSeries() as $series) { // c:ser $objWriter->startElement('c:ser'); - // c:idx + // c:ser > c:idx $objWriter->startElement('c:idx'); $objWriter->writeAttribute('val', $seriesIndex); $objWriter->endElement(); - // c:order + // c:ser > c:order $objWriter->startElement('c:order'); $objWriter->writeAttribute('val', $seriesIndex); $objWriter->endElement(); - // c:tx + // c:ser > c:tx $objWriter->startElement('c:tx'); $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex(1 + $seriesIndex) . '$1' : ''); $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords); $objWriter->endElement(); - // c:dLbls + // c:ser > c:dLbls $objWriter->startElement('c:dLbls'); - // c:txPr + // c:ser > c:dLbls > c:txPr $objWriter->startElement('c:txPr'); - // a:bodyPr + // c:ser > c:dLbls > c:txPr > a:bodyPr $objWriter->writeElement('a:bodyPr', null); - // a:lstStyle + // c:ser > c:dLbls > c:txPr > a:lstStyle $objWriter->writeElement('a:lstStyle', null); - // a:p + // c:ser > c:dLbls > c:txPr > a:p $objWriter->startElement('a:p'); - // a:pPr + // c:ser > c:dLbls > c:txPr > a:p > a:pPr $objWriter->startElement('a:pPr'); - // a:defRPr + // c:ser > c:dLbls > c:txPr > a:p > a:pPr > a:defRPr $objWriter->startElement('a:defRPr'); $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false')); @@ -974,59 +740,59 @@ protected function writeTypeArea(XMLWriter $objWriter, Area $subject, $includeSh $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); + $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '30000'); + $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-25000'); - if ($series->getFont()->isSuperScript() || $series->getFont()->isSubScript()) { - if ($series->getFont()->isSuperScript()) { - $objWriter->writeAttribute('baseline', '30000'); - } elseif ($series->getFont()->isSubScript()) { - $objWriter->writeAttribute('baseline', '-25000'); - } - } - - // Font - a:solidFill + // c:ser > c:dLbls > c:txPr > a:p > a:pPr > a:defRPr > a:solidFill $objWriter->startElement('a:solidFill'); - // a:srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $series->getFont()->getColor()->getRGB()); - $objWriter->endElement(); + $this->writeColor($objWriter, $series->getFont()->getColor()); + // c:ser > c:dLbls > c:txPr > a:p > a:pPr > a:defRPr > ## a:solidFill $objWriter->endElement(); - // Font - a:latin + // c:ser > c:dLbls > c:txPr > a:p > a:pPr > a:defRPr > a:latin $objWriter->startElement('a:latin'); $objWriter->writeAttribute('typeface', $series->getFont()->getName()); $objWriter->endElement(); + // c:ser > c:dLbls > c:txPr > a:p > a:pPr > ##a:defRPr $objWriter->endElement(); + // c:ser > c:dLbls > c:txPr > a:p > ##a:pPr $objWriter->endElement(); - // a:endParaRPr + // c:ser > c:dLbls > c:txPr > a:p > a:endParaRPr $objWriter->startElement('a:endParaRPr'); $objWriter->writeAttribute('lang', 'en-US'); $objWriter->writeAttribute('dirty', '0'); $objWriter->endElement(); + // c:ser > c:dLbls > c:txPr > ##a:p $objWriter->endElement(); + // c:ser > c:dLbls > ##c:txPr $objWriter->endElement(); - // c:showVal + // c:ser > c:dLbls > c:dLblPos + $this->writeElementWithValAttribute($objWriter, 'c:dLblPos', $series->getLabelPosition()); + + // c:ser > c:dLbls > c:showVal $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0'); - // c:showCatName + // c:ser > c:dLbls > c:showCatName $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0'); - // c:showSerName + // c:ser > c:dLbls > c:showSerName $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0'); - // c:showPercent + // c:ser > c:dLbls > c:showPercent $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0'); - // c:showLeaderLines + // c:ser > c:dLbls > c:showLeaderLines $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0'); + // c:ser > ##c:dLbls $objWriter->endElement(); if ($series->getFill()->getFillType() != Fill::FILL_NONE) { @@ -1083,9 +849,8 @@ protected function writeTypeArea(XMLWriter $objWriter, Area $subject, $includeSh $objWriter->endElement(); } - /** - * Write Type Bar3D + * Write Type Bar * * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer * @param \PhpOffice\PhpPresentation\Shape\Chart\Type\Bar $subject @@ -1109,7 +874,7 @@ protected function writeTypeBar(XMLWriter $objWriter, Bar $subject, $includeShee // Write series $seriesIndex = 0; - foreach ($subject->getData() as $series) { + foreach ($subject->getSeries() as $series) { // c:ser $objWriter->startElement('c:ser'); @@ -1185,22 +950,13 @@ protected function writeTypeBar(XMLWriter $objWriter, Bar $subject, $includeShee $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); - - if ($series->getFont()->isSuperScript() || $series->getFont()->isSubScript()) { - if ($series->getFont()->isSuperScript()) { - $objWriter->writeAttribute('baseline', '30000'); - } elseif ($series->getFont()->isSubScript()) { - $objWriter->writeAttribute('baseline', '-25000'); - } - } + $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '30000'); + $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-25000'); // Font - a:solidFill $objWriter->startElement('a:solidFill'); - // a:srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $series->getFont()->getColor()->getRGB()); - $objWriter->endElement(); + $this->writeColor($objWriter, $series->getFont()->getColor()); $objWriter->endElement(); @@ -1334,7 +1090,7 @@ protected function writeTypeBar3D(XMLWriter $objWriter, Bar3D $subject, $include // Write series $seriesIndex = 0; - foreach ($subject->getData() as $series) { + foreach ($subject->getSeries() as $series) { // c:ser $objWriter->startElement('c:ser'); @@ -1402,22 +1158,13 @@ protected function writeTypeBar3D(XMLWriter $objWriter, Bar3D $subject, $include $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); - - if ($series->getFont()->isSuperScript() || $series->getFont()->isSubScript()) { - if ($series->getFont()->isSuperScript()) { - $objWriter->writeAttribute('baseline', '30000'); - } elseif ($series->getFont()->isSubScript()) { - $objWriter->writeAttribute('baseline', '-25000'); - } - } + $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '30000'); + $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-25000'); // Font - a:solidFill $objWriter->startElement('a:solidFill'); - // a:srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $series->getFont()->getColor()->getRGB()); - $objWriter->endElement(); + $this->writeColor($objWriter, $series->getFont()->getColor()); $objWriter->endElement(); @@ -1457,7 +1204,7 @@ protected function writeTypeBar3D(XMLWriter $objWriter, Bar3D $subject, $include $objWriter->endElement(); - // c:spPr + // c:spPr if ($series->getFill()->getFillType() != Fill::FILL_NONE) { // c:spPr $objWriter->startElement('c:spPr'); @@ -1483,7 +1230,7 @@ protected function writeTypeBar3D(XMLWriter $objWriter, Bar3D $subject, $include $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : ''); $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords); $objWriter->endElement(); - + $objWriter->endElement(); ++$seriesIndex; @@ -1520,7 +1267,7 @@ protected function writeTypeBar3D(XMLWriter $objWriter, Bar3D $subject, $include /** * Write Type Pie * - * @param \PhpOffice\PhpPresentation\Shared\XMLWriter $objWriter XML Writer + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer * @param \PhpOffice\PhpPresentation\Shape\Chart\Type\Pie $subject * @param boolean $includeSheet * @throws \Exception @@ -1537,7 +1284,7 @@ protected function writeTypePie(XMLWriter $objWriter, Pie $subject, $includeShee // Write series $seriesIndex = 0; - foreach ($subject->getData() as $series) { + foreach ($subject->getSeries() as $series) { // c:ser $objWriter->startElement('c:ser'); @@ -1603,22 +1350,13 @@ protected function writeTypePie(XMLWriter $objWriter, Pie $subject, $includeShee $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); - - if ($series->getFont()->isSuperScript() || $series->getFont()->isSubScript()) { - if ($series->getFont()->isSuperScript()) { - $objWriter->writeAttribute('baseline', '30000'); - } elseif ($series->getFont()->isSubScript()) { - $objWriter->writeAttribute('baseline', '-25000'); - } - } + $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '30000'); + $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-25000'); // Font - a:solidFill $objWriter->startElement('a:solidFill'); - // a:srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $series->getFont()->getColor()->getRGB()); - $objWriter->endElement(); + $this->writeColor($objWriter, $series->getFont()->getColor()); $objWriter->endElement(); @@ -1706,7 +1444,7 @@ protected function writeTypePie3D(XMLWriter $objWriter, Pie3D $subject, $include // Write series $seriesIndex = 0; - foreach ($subject->getData() as $series) { + foreach ($subject->getSeries() as $series) { // c:ser $objWriter->startElement('c:ser'); @@ -1777,22 +1515,13 @@ protected function writeTypePie3D(XMLWriter $objWriter, Pie3D $subject, $include $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); - - if ($series->getFont()->isSuperScript() || $series->getFont()->isSubScript()) { - if ($series->getFont()->isSuperScript()) { - $objWriter->writeAttribute('baseline', '30000'); - } elseif ($series->getFont()->isSubScript()) { - $objWriter->writeAttribute('baseline', '-25000'); - } - } + $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '30000'); + $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-25000'); // Font - a:solidFill $objWriter->startElement('a:solidFill'); - // a:srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $series->getFont()->getColor()->getRGB()); - $objWriter->endElement(); + $this->writeColor($objWriter, $series->getFont()->getColor()); $objWriter->endElement(); @@ -1880,7 +1609,7 @@ protected function writeTypeLine(XMLWriter $objWriter, Line $subject, $includeSh // Write series $seriesIndex = 0; - foreach ($subject->getData() as $series) { + foreach ($subject->getSeries() as $series) { // c:ser $objWriter->startElement('c:ser'); @@ -1900,6 +1629,9 @@ protected function writeTypeLine(XMLWriter $objWriter, Line $subject, $includeSh $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords); $objWriter->endElement(); + // Marker + $this->writeSeriesMarker($objWriter, $series->getMarker()); + // c:dLbls $objWriter->startElement('c:dLbls'); @@ -1926,22 +1658,13 @@ protected function writeTypeLine(XMLWriter $objWriter, Line $subject, $includeSh $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); - - if ($series->getFont()->isSuperScript() || $series->getFont()->isSubScript()) { - if ($series->getFont()->isSuperScript()) { - $objWriter->writeAttribute('baseline', '30000'); - } elseif ($series->getFont()->isSubScript()) { - $objWriter->writeAttribute('baseline', '-25000'); - } - } + $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '30000'); + $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-25000'); // Font - a:solidFill $objWriter->startElement('a:solidFill'); - // a:srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $series->getFont()->getColor()->getRGB()); - $objWriter->endElement(); + $this->writeColor($objWriter, $series->getFont()->getColor()); $objWriter->endElement(); @@ -1979,16 +1702,17 @@ protected function writeTypeLine(XMLWriter $objWriter, Line $subject, $includeSh // c:showLeaderLines $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0'); + // > c:dLbls $objWriter->endElement(); - if ($series->getFill()->getFillType() != Fill::FILL_NONE) { - // c:spPr - $objWriter->startElement('c:spPr'); - // Write fill - $this->writeFill($objWriter, $series->getFill()); - // ## c:spPr - $objWriter->endElement(); - } + // c:spPr + $objWriter->startElement('c:spPr'); + // Write fill + $this->writeFill($objWriter, $series->getFill()); + // Write outline + $this->writeOutline($objWriter, $series->getOutline()); + // ## c:spPr + $objWriter->endElement(); // Write X axis data $axisXData = array_keys($series->getValues()); @@ -2060,7 +1784,7 @@ protected function writeTypeScatter(XMLWriter $objWriter, Scatter $subject, $inc // Write series $seriesIndex = 0; - foreach ($subject->getData() as $series) { + foreach ($subject->getSeries() as $series) { // c:ser $objWriter->startElement('c:ser'); @@ -2080,17 +1804,9 @@ protected function writeTypeScatter(XMLWriter $objWriter, Scatter $subject, $inc $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords); $objWriter->endElement(); - // c:marker - $objWriter->startElement('c:marker'); + // Marker + $this->writeSeriesMarker($objWriter, $series->getMarker()); - // c:marker - $objWriter->startElement('c:symbol'); - $objWriter->writeAttribute('val', 'none'); // Marker style - //$objWriter->writeAttribute('size', '7'); // Marker size - $objWriter->endElement(); - - $objWriter->endElement(); - // c:dLbls $objWriter->startElement('c:dLbls'); @@ -2117,22 +1833,13 @@ protected function writeTypeScatter(XMLWriter $objWriter, Scatter $subject, $inc $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); - - if ($series->getFont()->isSuperScript() || $series->getFont()->isSubScript()) { - if ($series->getFont()->isSuperScript()) { - $objWriter->writeAttribute('baseline', '30000'); - } elseif ($series->getFont()->isSubScript()) { - $objWriter->writeAttribute('baseline', '-25000'); - } - } + $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '30000'); + $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-25000'); // Font - a:solidFill $objWriter->startElement('a:solidFill'); - // a:srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $series->getFont()->getColor()->getRGB()); - $objWriter->endElement(); + $this->writeColor($objWriter, $series->getFont()->getColor()); $objWriter->endElement(); @@ -2175,14 +1882,14 @@ protected function writeTypeScatter(XMLWriter $objWriter, Scatter $subject, $inc $objWriter->endElement(); - if ($series->getFill()->getFillType() != Fill::FILL_NONE) { - // c:spPr - $objWriter->startElement('c:spPr'); - // Write fill - $this->writeFill($objWriter, $series->getFill()); - // ## c:spPr - $objWriter->endElement(); - } + // c:spPr + $objWriter->startElement('c:spPr'); + // Write fill + $this->writeFill($objWriter, $series->getFill()); + // Write outline + $this->writeOutline($objWriter, $series->getOutline()); + // ## c:spPr + $objWriter->endElement(); // Write X axis data $axisXData = array_keys($series->getValues()); @@ -2223,4 +1930,267 @@ protected function writeTypeScatter(XMLWriter $objWriter, Scatter $subject, $inc $objWriter->endElement(); } + /** + * Write chart relationships to XML format + * + * @param \PhpOffice\PhpPresentation\Shape\Chart $pChart + * @return string XML Output + * @throws \Exception + */ + public function writeChartRelationships(Chart $pChart) + { + // Create XML writer + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); + + // XML header + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + + // Relationships + $objWriter->startElement('Relationships'); + $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); + + // Write spreadsheet relationship? + if ($pChart->hasIncludedSpreadsheet()) { + $this->writeRelationship($objWriter, 1, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package', '../embeddings/' . $pChart->getIndexedFilename() . '.xlsx'); + } + + $objWriter->endElement(); + + // Return + return $objWriter->getData(); + } + + /** + * @param XMLWriter $objWriter + * @param Chart\Marker $oMarker + */ + protected function writeSeriesMarker(XMLWriter $objWriter, Chart\Marker $oMarker) + { + if ($oMarker->getSymbol() != Chart\Marker::SYMBOL_NONE) { + $markerSize = (int)$oMarker->getSize(); + if ($markerSize < 2) { + $markerSize = 2; + } + if ($markerSize > 72) { + $markerSize = 72; + } + + // c:marker + $objWriter->startElement('c:marker'); + + // c:marker > c:symbol + $objWriter->startElement('c:symbol'); + $objWriter->writeAttribute('val', $oMarker->getSymbol()); + $objWriter->endElement(); + /** + * c:marker > c:size + * Size in points + * @link : https://msdn.microsoft.com/en-us/library/hh658135(v=office.12).aspx + */ + $objWriter->startElement('c:size'); + $objWriter->writeAttribute('val', $markerSize); + $objWriter->endElement(); + + $objWriter->endElement(); + } + } + + /** + * @param XMLWriter $objWriter + * @param Chart\Axis $oAxis + * @param $typeAxis + */ + protected function writeAxis(XMLWriter $objWriter, Chart\Axis $oAxis, $typeAxis) + { + if ($typeAxis != Chart\Axis::AXIS_X && $typeAxis != Chart\Axis::AXIS_Y) { + return; + } + + if ($typeAxis == Chart\Axis::AXIS_X) { + $mainElement = 'c:catAx'; + $axIdVal = '52743552'; + $axPosVal = 'b'; + $crossAxVal = '52749440'; + } else { + $mainElement = 'c:valAx'; + $axIdVal = '52749440'; + $axPosVal = 'l'; + $crossAxVal = '52743552'; + } + + // $mainElement + $objWriter->startElement($mainElement); + + // $mainElement > c:axId + $objWriter->startElement('c:axId'); + $objWriter->writeAttribute('val', $axIdVal); + $objWriter->endElement(); + + // $mainElement > c:scaling + $objWriter->startElement('c:scaling'); + + // $mainElement > c:scaling > c:orientation + $objWriter->startElement('c:orientation'); + $objWriter->writeAttribute('val', 'minMax'); + $objWriter->endElement(); + + // $mainElement > ##c:scaling + $objWriter->endElement(); + + // $mainElement > c:axPos + $objWriter->startElement('c:axPos'); + $objWriter->writeAttribute('val', $axPosVal); + $objWriter->endElement(); + + $oMajorGridlines = $oAxis->getMajorGridlines(); + if ($oMajorGridlines instanceof Gridlines) { + $objWriter->startElement('c:majorGridlines'); + + $this->writeAxisGridlines($objWriter, $oMajorGridlines); + + $objWriter->endElement(); + } + + $oMinorGridlines = $oAxis->getMinorGridlines(); + if ($oMinorGridlines instanceof Gridlines) { + $objWriter->startElement('c:minorGridlines'); + + $this->writeAxisGridlines($objWriter, $oMinorGridlines); + + $objWriter->endElement(); + } + + // c:numFmt + $objWriter->startElement('c:numFmt'); + $objWriter->writeAttribute('formatCode', $oAxis->getFormatCode()); + $objWriter->writeAttribute('sourceLinked', '0'); + $objWriter->endElement(); + + // c:majorTickMark + $objWriter->startElement('c:majorTickMark'); + $objWriter->writeAttribute('val', 'none'); + $objWriter->endElement(); + + // c:tickLblPos + $objWriter->startElement('c:tickLblPos'); + $objWriter->writeAttribute('val', 'nextTo'); + $objWriter->endElement(); + + // c:txPr + $objWriter->startElement('c:txPr'); + + // a:bodyPr + $objWriter->writeElement('a:bodyPr', null); + + // a:lstStyle + $objWriter->writeElement('a:lstStyle', null); + + // a:p + $objWriter->startElement('a:p'); + + // a:pPr + $objWriter->startElement('a:pPr'); + + // a:defRPr + $objWriter->startElement('a:defRPr'); + + $objWriter->writeAttribute('b', ($oAxis->getFont()->isBold() ? 'true' : 'false')); + $objWriter->writeAttribute('i', ($oAxis->getFont()->isItalic() ? 'true' : 'false')); + $objWriter->writeAttribute('strike', ($oAxis->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); + $objWriter->writeAttribute('sz', ($oAxis->getFont()->getSize() * 100)); + $objWriter->writeAttribute('u', $oAxis->getFont()->getUnderline()); + $objWriter->writeAttributeIf($oAxis->getFont()->isSuperScript(), 'baseline', '30000'); + $objWriter->writeAttributeIf($oAxis->getFont()->isSubScript(), 'baseline', '-25000'); + + // Font - a:solidFill + $objWriter->startElement('a:solidFill'); + + $this->writeColor($objWriter, $oAxis->getFont()->getColor()); + + $objWriter->endElement(); + + // Font - a:latin + $objWriter->startElement('a:latin'); + $objWriter->writeAttribute('typeface', $oAxis->getFont()->getName()); + $objWriter->endElement(); + + $objWriter->endElement(); + + // ## a:pPr + $objWriter->endElement(); + + // a:r + $objWriter->startElement('a:r'); + + // a:rPr + $objWriter->startElement('a:rPr'); + $objWriter->writeAttribute('lang', 'en-US'); + $objWriter->writeAttribute('dirty', '0'); + $objWriter->endElement(); + + // a:t + $objWriter->writeElement('a:t', $oAxis->getTitle()); + + // ## a:r + $objWriter->endElement(); + + // a:endParaRPr + $objWriter->startElement('a:endParaRPr'); + $objWriter->writeAttribute('lang', 'en-US'); + $objWriter->writeAttribute('dirty', '0'); + $objWriter->endElement(); + + // ## a:p + $objWriter->endElement(); + + // ## c:txPr + $objWriter->endElement(); + + // c:crossAx + $objWriter->startElement('c:crossAx'); + $objWriter->writeAttribute('val', $crossAxVal); + $objWriter->endElement(); + + // c:crosses + $objWriter->startElement('c:crosses'); + $objWriter->writeAttribute('val', 'autoZero'); + $objWriter->endElement(); + + if ($typeAxis == Chart\Axis::AXIS_X) { + // c:lblAlgn + $objWriter->startElement('c:lblAlgn'); + $objWriter->writeAttribute('val', 'ctr'); + $objWriter->endElement(); + + // c:lblOffset + $objWriter->startElement('c:lblOffset'); + $objWriter->writeAttribute('val', '100'); + $objWriter->endElement(); + } + + if ($typeAxis == Chart\Axis::AXIS_Y) { + // c:crossBetween + $objWriter->startElement('c:crossBetween'); + $objWriter->writeAttribute('val', 'between'); + $objWriter->endElement(); + } + + $objWriter->endElement(); + } + + /** + * @param XMLWriter $objWriter + * @param Gridlines $oGridlines + */ + protected function writeAxisGridlines(XMLWriter $objWriter, Gridlines $oGridlines) + { + // c:spPr + $objWriter->startElement('c:spPr'); + + // Outline + $this->writeOutline($objWriter, $oGridlines->getOutline()); + + // ##c:spPr + $objWriter->endElement(); + } } diff --git a/src/PhpPresentation/Writer/PowerPoint2007/PptComments.php b/src/PhpPresentation/Writer/PowerPoint2007/PptComments.php new file mode 100644 index 000000000..badf1ac7e --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/PptComments.php @@ -0,0 +1,84 @@ +getPresentation()->getAllSlides() as $numSlide => $oSlide) { + $contentXml = $this->writeSlideComments($oSlide); + if (empty($contentXml)) { + continue; + } + $this->getZip()->addFromString('ppt/comments/comment'.($numSlide + 1).'.xml', $contentXml); + } + return $this->getZip(); + } + + /** + * @param Slide $oSlide + * @return string + */ + protected function writeSlideComments(Slide $oSlide) + { + /** + * @var Comment[] + */ + $arrayComment = array(); + foreach ($oSlide->getShapeCollection() as $oShape) { + if ($oShape instanceof Comment) { + $arrayComment[] = $oShape; + } + } + + if (empty($arrayComment)) { + return ''; + } + + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + + // p:cmLst + $objWriter->startElement('p:cmLst'); + $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main'); + + foreach ($arrayComment as $idxComment => $oComment) { + $oAuthor = $oComment->getAuthor(); + + // p:cmLst > p:cm + $objWriter->startElement('p:cm'); + $objWriter->writeAttribute('authorId', $oAuthor instanceof Comment\Author ? $oAuthor->getIndex() : 0); + $objWriter->writeAttribute('dt', date('Y-m-d\TH:i:s.000000000', $oComment->getDate())); + $objWriter->writeAttribute('idx', $idxComment); + + // p:cmLst > p:cm > p:pos + // Uses 1/8pt for positionning + // @link : https://social.msdn.microsoft.com/Forums/fr-FR/ebdc12f2-0cff-4fa8-b901-fa6e3198364e/comment-position-units + $objWriter->startElement('p:pos'); + $objWriter->writeAttribute('x', (int) CommonDrawing::pixelsToPoints($oComment->getOffsetX() * 8)); + $objWriter->writeAttribute('y', (int) CommonDrawing::pixelsToPoints($oComment->getOffsetY() * 8)); + $objWriter->endElement(); + + // p:cmLst > p:cm > p:text + $objWriter->writeElement('p:text', $oComment->getText()); + + // p:cmLst > ## p:cm + $objWriter->endElement(); + } + + // ## p:cmLst + $objWriter->endElement(); + + return $objWriter->getData(); + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/PptMedia.php b/src/PhpPresentation/Writer/PowerPoint2007/PptMedia.php new file mode 100644 index 000000000..0196e391f --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/PptMedia.php @@ -0,0 +1,24 @@ +getDrawingHashTable()->count(); ++$i) { + $shape = $this->getDrawingHashTable()->getByIndex($i); + if (!$shape instanceof AbstractDrawingAdapter) { + continue; + } + $this->getZip()->addFromString('ppt/media/' . $shape->getIndexedFilename(), $shape->getContents()); + } + + return $this->getZip(); + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/PptPresProps.php b/src/PhpPresentation/Writer/PowerPoint2007/PptPresProps.php new file mode 100644 index 000000000..df1234979 --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/PptPresProps.php @@ -0,0 +1,71 @@ +oPresentation->getPresentationProperties(); + + // Create XML writer + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); + + // XML header + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + + // p:presentationPr + $objWriter->startElement('p:presentationPr'); + $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main'); + + // p:presentationPr > p:showPr + if ($presentationPpts->isLoopContinuouslyUntilEsc()) { + $objWriter->startElement('p:showPr'); + $objWriter->writeAttribute('loop', '1'); + $objWriter->endElement(); + } + + // p:extLst + $objWriter->startElement('p:extLst'); + + // p:ext + $objWriter->startElement('p:ext'); + $objWriter->writeAttribute('uri', '{E76CE94A-603C-4142-B9EB-6D1370010A27}'); + + // p14:discardImageEditData + $objWriter->startElement('p14:discardImageEditData'); + $objWriter->writeAttribute('xmlns:p14', 'http://schemas.microsoft.com/office/powerpoint/2010/main'); + $objWriter->writeAttribute('val', '0'); + $objWriter->endElement(); + + // > p:ext + $objWriter->endElement(); + + // p:ext + $objWriter->startElement('p:ext'); + $objWriter->writeAttribute('uri', '{D31A062A-798A-4329-ABDD-BBA856620510}'); + + // p14:defaultImageDpi + $objWriter->startElement('p14:defaultImageDpi'); + $objWriter->writeAttribute('xmlns:p14', 'http://schemas.microsoft.com/office/powerpoint/2010/main'); + $objWriter->writeAttribute('val', '220'); + $objWriter->endElement(); + + // > p:ext + $objWriter->endElement(); + // > p:extLst + $objWriter->endElement(); + // > p:presentationPr + $objWriter->endElement(); + + $this->getZip()->addFromString('ppt/presProps.xml', $objWriter->getData()); + + return $this->getZip(); + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/PptPresentation.php b/src/PhpPresentation/Writer/PowerPoint2007/PptPresentation.php new file mode 100644 index 000000000..5e9b3305a --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/PptPresentation.php @@ -0,0 +1,182 @@ +startDocument('1.0', 'UTF-8', 'yes'); + + // p:presentation + $objWriter->startElement('p:presentation'); + $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main'); + + // p:sldMasterIdLst + $objWriter->startElement('p:sldMasterIdLst'); + + // Add slide masters + $relationId = 1; + $slideMasterId = 2147483648; + + $countMasterSlides = count($this->oPresentation->getAllMasterSlides()); + for ($inc = 1; $inc <= $countMasterSlides; $inc++) { + // p:sldMasterId + $objWriter->startElement('p:sldMasterId'); + $objWriter->writeAttribute('id', $slideMasterId); + $objWriter->writeAttribute('r:id', 'rId' . $relationId++); + $objWriter->endElement(); + + // Increase identifier + $slideMasterId += 12; + } + $objWriter->endElement(); + + // theme + $relationId++; + + // p:sldIdLst + $objWriter->startElement('p:sldIdLst'); + // Write slides + $slideCount = $this->oPresentation->getSlideCount(); + for ($i = 0; $i < $slideCount; ++$i) { + // p:sldId + $objWriter->startElement('p:sldId'); + $objWriter->writeAttribute('id', ($i + 256)); + $objWriter->writeAttribute('r:id', 'rId' . ($i + $relationId)); + $objWriter->endElement(); + } + $objWriter->endElement(); + + // p:sldSz + $objWriter->startElement('p:sldSz'); + $objWriter->writeAttribute('cx', $this->oPresentation->getLayout()->getCX()); + $objWriter->writeAttribute('cy', $this->oPresentation->getLayout()->getCY()); + if ($this->oPresentation->getLayout()->getDocumentLayout() != DocumentLayout::LAYOUT_CUSTOM) { + $objWriter->writeAttribute('type', $this->oPresentation->getLayout()->getDocumentLayout()); + } + $objWriter->endElement(); + + // p:notesSz + $objWriter->startElement('p:notesSz'); + $objWriter->writeAttribute('cx', '6858000'); + $objWriter->writeAttribute('cy', '9144000'); + $objWriter->endElement(); + + $objWriter->writeRaw(' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +'); + + $objWriter->endElement(); + + $this->oZip->addFromString('ppt/presentation.xml', $objWriter->getData()); + + // Return + return $this->oZip; + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/PptProps.php b/src/PhpPresentation/Writer/PowerPoint2007/PptProps.php deleted file mode 100644 index 62018e187..000000000 --- a/src/PhpPresentation/Writer/PowerPoint2007/PptProps.php +++ /dev/null @@ -1,176 +0,0 @@ -getPresentationProperties(); - - // Create XML writer - $objWriter = $this->getXMLWriter(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // p:presentationPr - $objWriter->startElement('p:presentationPr'); - $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); - $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); - $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main'); - - // p:presentationPr > p:showPr - if ($presentationPpts->isLoopContinuouslyUntilEsc()) { - $objWriter->startElement('p:showPr'); - $objWriter->writeAttribute('loop', '1'); - $objWriter->endElement(); - } - - // p:extLst - $objWriter->startElement('p:extLst'); - - // p:ext - $objWriter->startElement('p:ext'); - $objWriter->writeAttribute('uri', '{E76CE94A-603C-4142-B9EB-6D1370010A27}'); - - // p14:discardImageEditData - $objWriter->startElement('p14:discardImageEditData'); - $objWriter->writeAttribute('xmlns:p14', 'http://schemas.microsoft.com/office/powerpoint/2010/main'); - $objWriter->writeAttribute('val', '0'); - $objWriter->endElement(); - - // > p:ext - $objWriter->endElement(); - - // p:ext - $objWriter->startElement('p:ext'); - $objWriter->writeAttribute('uri', '{D31A062A-798A-4329-ABDD-BBA856620510}'); - - // p14:defaultImageDpi - $objWriter->startElement('p14:defaultImageDpi'); - $objWriter->writeAttribute('xmlns:p14', 'http://schemas.microsoft.com/office/powerpoint/2010/main'); - $objWriter->writeAttribute('val', '220'); - $objWriter->endElement(); - - // > p:ext - $objWriter->endElement(); - // > p:extLst - $objWriter->endElement(); - // > p:presentationPr - $objWriter->endElement(); - - return $objWriter->getData(); - } - - /** - * Write ppt/tableStyles.xml to XML format - * - * @return string XML Output - * @throws \Exception - */ - public function writeTableStyles() - { - // Create XML writer - $objWriter = $this->getXMLWriter(); - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // a:tblStyleLst - $objWriter->startElement('a:tblStyleLst'); - $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); - $objWriter->writeAttribute('def', '{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}'); - $objWriter->endElement(); - - return $objWriter->getData(); - } - - /** - * Write ppt/viewProps.xml to XML format - * - * @return string XML Output - * @throws \Exception - */ - public function writeViewProps(PhpPresentation $oPhpPresentation) - { - // Create XML writer - $objWriter = $this->getXMLWriter(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // p:viewPr - $objWriter->startElement('p:viewPr'); - $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); - $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); - $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main'); - $objWriter->writeAttribute('showComments', '0'); - - // p:viewPr > p:slideViewPr - $objWriter->startElement('p:slideViewPr'); - - // p:viewPr > p:slideViewPr > p:cSldViewPr - $objWriter->startElement('p:cSldViewPr'); - - // p:viewPr > p:slideViewPr > p:cSldViewPr > p:cViewPr - $objWriter->startElement('p:cViewPr'); - - // p:viewPr > p:slideViewPr > p:cSldViewPr > p:cViewPr > p:scale - $objWriter->startElement('p:scale'); - - $objWriter->startElement('a:sx'); - $objWriter->writeAttribute('d', '100'); - $objWriter->writeAttribute('n', (int)($oPhpPresentation->getZoom() * 100)); - $objWriter->endElement(); - - $objWriter->startElement('a:sy'); - $objWriter->writeAttribute('d', '100'); - $objWriter->writeAttribute('n', (int)($oPhpPresentation->getZoom() * 100)); - $objWriter->endElement(); - - // > // p:viewPr > p:slideViewPr > p:cSldViewPr > p:cViewPr > p:scale - $objWriter->endElement(); - - $objWriter->startElement('p:origin'); - $objWriter->writeAttribute('x', '0'); - $objWriter->writeAttribute('y', '0'); - $objWriter->endElement(); - - // > // p:viewPr > p:slideViewPr > p:cSldViewPr > p:cViewPr - $objWriter->endElement(); - - // > // p:viewPr > p:slideViewPr > p:cSldViewPr - $objWriter->endElement(); - - // > // p:viewPr > p:slideViewPr - $objWriter->endElement(); - - // > // p:viewPr - $objWriter->endElement(); - - return $objWriter->getData(); - } -} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/PptSlideLayouts.php b/src/PhpPresentation/Writer/PowerPoint2007/PptSlideLayouts.php new file mode 100644 index 000000000..00f3d3025 --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/PptSlideLayouts.php @@ -0,0 +1,158 @@ +oPresentation->getAllMasterSlides() as $oSlideMaster) { + foreach ($oSlideMaster->getAllSlideLayouts() as $oSlideLayout) { + $this->oZip->addFromString('ppt/slideLayouts/_rels/slideLayout' . $oSlideLayout->layoutNr . '.xml.rels', $this->writeSlideLayoutRelationships($oSlideMaster->getRelsIndex())); + $this->oZip->addFromString('ppt/slideLayouts/slideLayout' . $oSlideLayout->layoutNr . '.xml', $this->writeSlideLayout($oSlideLayout)); + } + } + + return $this->oZip; + } + + + /** + * Write slide layout relationships to XML format + * + * @param int $masterId + * @return string XML Output + * @throws \Exception + */ + public function writeSlideLayoutRelationships($masterId = 1) + { + // Create XML writer + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); + + // XML header + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + + // Relationships + $objWriter->startElement('Relationships'); + $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); + + // Write slideMaster relationship + $this->writeRelationship($objWriter, 1, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster', '../slideMasters/slideMaster' . $masterId . '.xml'); + + $objWriter->endElement(); + + // Return + return $objWriter->getData(); + } + + /** + * Write slide to XML format + * + * @param \PhpOffice\PhpPresentation\Slide\SlideLayout $pSlideLayout + * @return string XML Output + * @throws \Exception + */ + public function writeSlideLayout(SlideLayout $pSlideLayout) + { + // Create XML writer + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); + // XML header + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + // p:sldLayout + $objWriter->startElement('p:sldLayout'); + $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main'); + $objWriter->writeAttribute('preserve', 1); + // p:sldLayout\p:cSld + $objWriter->startElement('p:cSld'); + $objWriter->writeAttributeIf($pSlideLayout->getLayoutName() != '', 'name', $pSlideLayout->getLayoutName()); + // Background + $this->writeSlideBackground($pSlideLayout, $objWriter); + // p:sldLayout\p:cSld\p:spTree + $objWriter->startElement('p:spTree'); + // p:sldLayout\p:cSld\p:spTree\p:nvGrpSpPr + $objWriter->startElement('p:nvGrpSpPr'); + // p:sldLayout\p:cSld\p:spTree\p:nvGrpSpPr\p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('id', '1'); + $objWriter->writeAttribute('name', ''); + $objWriter->endElement(); + // p:sldLayout\p:cSld\p:spTree\p:nvGrpSpPr\p:cNvGrpSpPr + $objWriter->writeElement('p:cNvGrpSpPr', null); + // p:sldLayout\p:cSld\p:spTree\p:nvGrpSpPr\p:nvPr + $objWriter->writeElement('p:nvPr', null); + // p:sldLayout\p:cSld\p:spTree\p:nvGrpSpPr + $objWriter->endElement(); + // p:sldLayout\p:cSld\p:spTree\p:grpSpPr + $objWriter->startElement('p:grpSpPr'); + // p:sldLayout\p:cSld\p:spTree\p:grpSpPr\a:xfrm + $objWriter->startElement('a:xfrm'); + // p:sldLayout\p:cSld\p:spTree\p:grpSpPr\a:xfrm\a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pSlideLayout->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($pSlideLayout->getOffsetY())); + $objWriter->endElement(); + // p:sldLayout\p:cSld\p:spTree\p:grpSpPr\a:xfrm\a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($pSlideLayout->getExtentX())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($pSlideLayout->getExtentY())); + $objWriter->endElement(); + // p:sldLayout\p:cSld\p:spTree\p:grpSpPr\a:xfrm\a:chOff + $objWriter->startElement('a:chOff'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pSlideLayout->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($pSlideLayout->getOffsetY())); + $objWriter->endElement(); + // p:sldLayout\p:cSld\p:spTree\p:grpSpPr\a:xfrm\a:chExt + $objWriter->startElement('a:chExt'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($pSlideLayout->getExtentX())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($pSlideLayout->getExtentY())); + $objWriter->endElement(); + // p:sldLayout\p:cSld\p:spTree\p:grpSpPr\a:xfrm\ + $objWriter->endElement(); + // p:sldLayout\p:cSld\p:spTree\p:grpSpPr\ + $objWriter->endElement(); + + // Loop shapes + $this->writeShapeCollection($objWriter, $pSlideLayout->getShapeCollection()); + // p:sldLayout\p:cSld\p:spTree\ + $objWriter->endElement(); + // p:sldLayout\p:cSld\ + $objWriter->endElement(); + + // p:sldLayout\p:clrMapOvr + $objWriter->startElement('p:clrMapOvr'); + $arrayDiff = array_diff_assoc(ColorMap::$mappingDefault, $pSlideLayout->colorMap->getMapping()); + if (!empty($arrayDiff)) { + // p:sldLayout\p:clrMapOvr\a:overrideClrMapping + $objWriter->startElement('a:overrideClrMapping'); + foreach ($pSlideLayout->colorMap->getMapping() as $n => $v) { + $objWriter->writeAttribute($n, $v); + } + $objWriter->endElement(); + } else { + // p:sldLayout\p:clrMapOvr\a:masterClrMapping + $objWriter->writeElement('a:masterClrMapping'); + } + // p:sldLayout\p:clrMapOvr\ + $objWriter->endElement(); + + if (!is_null($pSlideLayout->getTransition())) { + $this->writeTransition($objWriter, $pSlideLayout->getTransition()); + } + + // p:sldLayout\ + $objWriter->endElement(); + + return $objWriter->getData(); + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/PptSlideMasters.php b/src/PhpPresentation/Writer/PowerPoint2007/PptSlideMasters.php new file mode 100644 index 000000000..a091e9274 --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/PptSlideMasters.php @@ -0,0 +1,219 @@ +oPresentation->getAllMasterSlides() as $oMasterSlide) { + // Add the relations from the masterSlide to the ZIP file + $this->oZip->addFromString('ppt/slideMasters/_rels/slideMaster' . $oMasterSlide->getRelsIndex() . '.xml.rels', $this->writeSlideMasterRelationships($oMasterSlide)); + // Add the information from the masterSlide to the ZIP file + $this->oZip->addFromString('ppt/slideMasters/slideMaster' . $oMasterSlide->getRelsIndex() . '.xml', $this->writeSlideMaster($oMasterSlide)); + } + + return $this->oZip; + } + + /** + * Write slide master relationships to XML format + * + * @param SlideMaster $oMasterSlide + * @return string XML Output + * @throws \Exception + * @internal param int $masterId Master slide id + */ + public function writeSlideMasterRelationships(SlideMaster $oMasterSlide) + { + // Create XML writer + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); + // XML header + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + // Relationships + $objWriter->startElement('Relationships'); + $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); + // Starting relation id + $relId = 0; + // Write all the relations to the Layout Slides + foreach ($oMasterSlide->getAllSlideLayouts() as $slideLayout) { + $this->writeRelationship($objWriter, ++$relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout', '../slideLayouts/slideLayout' . $slideLayout->layoutNr . '.xml'); + // Save the used relationId + $slideLayout->relationId = 'rId' . $relId; + } + // Write drawing relationships? + $this->writeDrawingRelations($oMasterSlide, $objWriter, $relId); + // TODO: Write hyperlink relationships? + // TODO: Write comment relationships + // Relationship theme/theme1.xml + $this->writeRelationship($objWriter, ++$relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme', '../theme/theme' . $oMasterSlide->getRelsIndex() . '.xml'); + $objWriter->endElement(); + // Return + return $objWriter->getData(); + } + + /** + * Write slide to XML format + * + * @param \PhpOffice\PhpPresentation\Slide\SlideMaster $pSlide + * @return string XML Output + * @throws \Exception + */ + public function writeSlideMaster(SlideMaster $pSlide) + { + // Create XML writer + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); + // XML header + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + // p:sldMaster + $objWriter->startElement('p:sldMaster'); + $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main'); + // p:sldMaster\p:cSld + $objWriter->startElement('p:cSld'); + // Background + $this->writeSlideBackground($pSlide, $objWriter); + // p:sldMaster\p:cSld\p:spTree + $objWriter->startElement('p:spTree'); + // p:sldMaster\p:cSld\p:spTree\p:nvGrpSpPr + $objWriter->startElement('p:nvGrpSpPr'); + // p:sldMaster\p:cSld\p:spTree\p:nvGrpSpPr\p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('id', '1'); + $objWriter->writeAttribute('name', ''); + $objWriter->endElement(); + // p:sldMaster\p:cSld\p:spTree\p:nvGrpSpPr\p:cNvGrpSpPr + $objWriter->writeElement('p:cNvGrpSpPr', null); + // p:sldMaster\p:cSld\p:spTree\p:nvGrpSpPr\p:nvPr + $objWriter->writeElement('p:nvPr', null); + // p:sldMaster\p:cSld\p:spTree\p:nvGrpSpPr + $objWriter->endElement(); + // p:sldMaster\p:cSld\p:spTree\p:grpSpPr + $objWriter->startElement('p:grpSpPr'); + // p:sldMaster\p:cSld\p:spTree\p:grpSpPr\a:xfrm + $objWriter->startElement('a:xfrm'); + // p:sldMaster\p:cSld\p:spTree\p:grpSpPr\a:xfrm\a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', 0); + $objWriter->writeAttribute('y', 0); + $objWriter->endElement(); + // p:sldMaster\p:cSld\p:spTree\p:grpSpPr\a:xfrm\a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', 0); + $objWriter->writeAttribute('cy', 0); + $objWriter->endElement(); + // p:sldMaster\p:cSld\p:spTree\p:grpSpPr\a:xfrm\a:chOff + $objWriter->startElement('a:chOff'); + $objWriter->writeAttribute('x', 0); + $objWriter->writeAttribute('y', 0); + $objWriter->endElement(); + // p:sldMaster\p:cSld\p:spTree\p:grpSpPr\a:xfrm\a:chExt + $objWriter->startElement('a:chExt'); + $objWriter->writeAttribute('cx', 0); + $objWriter->writeAttribute('cy', 0); + $objWriter->endElement(); + // p:sldMaster\p:cSld\p:spTree\p:grpSpPr\a:xfrm\ + $objWriter->endElement(); + // p:sldMaster\p:cSld\p:spTree\p:grpSpPr\ + $objWriter->endElement(); + // Loop shapes + $this->writeShapeCollection($objWriter, $pSlide->getShapeCollection()); + // p:sldMaster\p:cSld\p:spTree\ + $objWriter->endElement(); + // p:sldMaster\p:cSld\ + $objWriter->endElement(); + + // p:sldMaster\p:clrMap + $objWriter->startElement('p:clrMap'); + foreach ($pSlide->colorMap->getMapping() as $n => $v) { + $objWriter->writeAttribute($n, $v); + } + $objWriter->endElement(); + // p:sldMaster\p:clrMap\ + + // p:sldMaster\p:sldLayoutIdLst + $objWriter->startElement('p:sldLayoutIdLst'); + foreach ($pSlide->getAllSlideLayouts() as $layout) { + /* @var $layout Slide\SlideLayout */ + $objWriter->startElement('p:sldLayoutId'); + $objWriter->writeAttribute('id', $layout->layoutId); + $objWriter->writeAttribute('r:id', $layout->relationId); + $objWriter->endElement(); + } + $objWriter->endElement(); + // p:sldMaster\p:sldLayoutIdLst\ + + // p:sldMaster\p:txStyles + $objWriter->startElement('p:txStyles'); + foreach (array( + 'p:titleStyle' => $pSlide->getTextStyles()->getTitleStyle(), + 'p:bodyStyle' => $pSlide->getTextStyles()->getBodyStyle(), + 'p:otherStyle' => $pSlide->getTextStyles()->getOtherStyle() + ) as $startElement => $stylesArray) { + // titleStyle + $objWriter->startElement($startElement); + foreach ($stylesArray as $lvl => $oParagraph) { + /** @var RichText\Paragraph $oParagraph */ + $elementName = ($lvl == 0 ? 'a:defPPr' : 'a:lvl' . $lvl . 'pPr'); + $objWriter->startElement($elementName); + $objWriter->writeAttribute('algn', $oParagraph->getAlignment()->getHorizontal()); + $objWriter->writeAttributeIf( + $oParagraph->getAlignment()->getMarginLeft() != 0, + 'marL', + CommonDrawing::pixelsToEmu($oParagraph->getAlignment()->getMarginLeft()) + ); + $objWriter->writeAttributeIf( + $oParagraph->getAlignment()->getMarginRight() != 0, + 'marR', + CommonDrawing::pixelsToEmu($oParagraph->getAlignment()->getMarginRight()) + ); + $objWriter->writeAttributeIf( + $oParagraph->getAlignment()->getIndent() != 0, + 'indent', + CommonDrawing::pixelsToEmu($oParagraph->getAlignment()->getIndent()) + ); + $objWriter->startElement('a:defRPr'); + $objWriter->writeAttributeIf($oParagraph->getFont()->getSize() != 10, 'sz', $oParagraph->getFont()->getSize() * 100); + $objWriter->writeAttributeIf($oParagraph->getFont()->isBold(), 'b', 1); + $objWriter->writeAttributeIf($oParagraph->getFont()->isItalic(), 'i', 1); + $objWriter->writeAttribute('kern', '1200'); + if ($oParagraph->getFont()->getColor() instanceof SchemeColor) { + $objWriter->startElement('a:solidFill'); + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', $oParagraph->getFont()->getColor()->getValue()); + $objWriter->endElement(); + $objWriter->endElement(); + } + $objWriter->endElement(); + $objWriter->endElement(); + } + $objWriter->writeElement('a:extLst'); + $objWriter->endElement(); + } + $objWriter->endElement(); + // p:sldMaster\p:txStyles\ + + if (!is_null($pSlide->getTransition())) { + $this->writeTransition($objWriter, $pSlide->getTransition()); + } + + // p:sldMaster\ + $objWriter->endElement(); + + return $objWriter->getData(); + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/PptSlides.php b/src/PhpPresentation/Writer/PowerPoint2007/PptSlides.php new file mode 100644 index 000000000..911be5463 --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/PptSlides.php @@ -0,0 +1,2189 @@ +oPresentation->getAllSlides() as $idx => $oSlide) { + $this->oZip->addFromString('ppt/slides/_rels/slide' . ($idx + 1) . '.xml.rels', $this->writeSlideRelationships($oSlide)); + $this->oZip->addFromString('ppt/slides/slide' . ($idx + 1) . '.xml', $this->writeSlide($oSlide)); + + // Add note slide + if ($oSlide->getNote()->getShapeCollection()->count() > 0) { + $this->oZip->addFromString('ppt/notesSlides/notesSlide' . ($idx + 1) . '.xml', $this->writeNote($oSlide->getNote())); + } + + // Add background image slide + $oBkgImage = $oSlide->getBackground(); + if ($oBkgImage instanceof Image) { + $this->oZip->addFromString('ppt/media/'.$oBkgImage->getIndexedFilename($idx), file_get_contents($oBkgImage->getPath())); + } + } + + return $this->oZip; + } + + /** + * Write slide relationships to XML format + * + * @param \PhpOffice\PhpPresentation\Slide $pSlide + * @return string XML Output + * @throws \Exception + */ + protected function writeSlideRelationships(Slide $pSlide) + { + //@todo Group all getShapeCollection()->getIterator + + // Create XML writer + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); + + // XML header + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + + // Relationships + $objWriter->startElement('Relationships'); + $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); + + // Starting relation id + $relId = 1; + $idxSlide = $pSlide->getParent()->getIndex($pSlide); + + // Write slideLayout relationship + if ($pSlide->getSlideLayout()) { + $layoutId = $pSlide->getSlideLayout()->layoutNr; + $this->writeRelationship($objWriter, $relId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout', '../slideLayouts/slideLayout' . $layoutId . '.xml'); + } + + // Write drawing relationships? + if ($pSlide->getShapeCollection()->count() > 0) { + // Loop trough images and write relationships + $iterator = $pSlide->getShapeCollection()->getIterator(); + while ($iterator->valid()) { + if ($iterator->current() instanceof Media) { + // Write relationship for image drawing + $this->writeRelationship($objWriter, $relId, 'http://schemas.microsoft.com/office/2007/relationships/media', '../media/' . $iterator->current()->getIndexedFilename()); + $iterator->current()->relationId = 'rId' . $relId; + ++$relId; + } elseif ($iterator->current() instanceof ShapeDrawing\AbstractDrawingAdapter) { + // Write relationship for image drawing + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', '../media/' . $iterator->current()->getIndexedFilename()); + $iterator->current()->relationId = 'rId' . $relId; + ++$relId; + } elseif ($iterator->current() instanceof ShapeChart) { + // Write relationship for chart drawing + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart', '../charts/' . $iterator->current()->getIndexedFilename()); + + $iterator->current()->relationId = 'rId' . $relId; + + ++$relId; + } elseif ($iterator->current() instanceof Group) { + $iterator2 = $iterator->current()->getShapeCollection()->getIterator(); + while ($iterator2->valid()) { + if ($iterator->current() instanceof Media) { + // Write relationship for image drawing + $this->writeRelationship($objWriter, $relId, 'http://schemas.microsoft.com/office/2007/relationships/media', '../media/' . $iterator->current()->getIndexedFilename()); + $iterator->current()->relationId = 'rId' . $relId; + ++$relId; + } elseif ($iterator->current() instanceof ShapeDrawing\AbstractDrawingAdapter) { + // Write relationship for image drawing + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', '../media/' . $iterator2->current()->getIndexedFilename()); + $iterator2->current()->relationId = 'rId' . $relId; + + ++$relId; + } elseif ($iterator2->current() instanceof ShapeChart) { + // Write relationship for chart drawing + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart', '../charts/' . $iterator2->current()->getIndexedFilename()); + $iterator2->current()->relationId = 'rId' . $relId; + + ++$relId; + } + $iterator2->next(); + } + } + + $iterator->next(); + } + } + + // Write background relationships? + $oBackground = $pSlide->getBackground(); + if ($oBackground instanceof Image) { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', '../media/' . $oBackground->getIndexedFilename($idxSlide)); + $oBackground->relationId = 'rId' . $relId; + ++$relId; + } + + // Write hyperlink relationships? + if ($pSlide->getShapeCollection()->count() > 0) { + // Loop trough hyperlinks and write relationships + $iterator = $pSlide->getShapeCollection()->getIterator(); + while ($iterator->valid()) { + // Hyperlink on shape + if ($iterator->current()->hasHyperlink()) { + // Write relationship for hyperlink + $hyperlink = $iterator->current()->getHyperlink(); + $hyperlink->relationId = 'rId' . $relId; + + if (!$hyperlink->isInternal()) { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External'); + } else { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml'); + } + + ++$relId; + } + + // Hyperlink on rich text run + if ($iterator->current() instanceof RichText) { + foreach ($iterator->current()->getParagraphs() as $paragraph) { + foreach ($paragraph->getRichTextElements() as $element) { + if ($element instanceof Run || $element instanceof RunTextElement) { + if ($element->hasHyperlink()) { + // Write relationship for hyperlink + $hyperlink = $element->getHyperlink(); + $hyperlink->relationId = 'rId' . $relId; + + if (!$hyperlink->isInternal()) { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External'); + } else { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml'); + } + + ++$relId; + } + } + } + } + } + + // Hyperlink in table + if ($iterator->current() instanceof ShapeTable) { + // Rows + $countRows = count($iterator->current()->getRows()); + for ($row = 0; $row < $countRows; $row++) { + // Cells in rows + $countCells = count($iterator->current()->getRow($row)->getCells()); + for ($cell = 0; $cell < $countCells; $cell++) { + $currentCell = $iterator->current()->getRow($row)->getCell($cell); + // Paragraphs in cell + foreach ($currentCell->getParagraphs() as $paragraph) { + // RichText in paragraph + foreach ($paragraph->getRichTextElements() as $element) { + // Run or Text in RichText + if ($element instanceof Run || $element instanceof TextElement) { + if ($element->hasHyperlink()) { + // Write relationship for hyperlink + $hyperlink = $element->getHyperlink(); + $hyperlink->relationId = 'rId' . $relId; + + if (!$hyperlink->isInternal()) { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External'); + } else { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml'); + } + + ++$relId; + } + } + } + } + } + } + } + + if ($iterator->current() instanceof Group) { + $iterator2 = $pSlide->getShapeCollection()->getIterator(); + while ($iterator2->valid()) { + // Hyperlink on shape + if ($iterator2->current()->hasHyperlink()) { + // Write relationship for hyperlink + $hyperlink = $iterator2->current()->getHyperlink(); + $hyperlink->relationId = 'rId' . $relId; + + if (!$hyperlink->isInternal()) { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External'); + } else { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml'); + } + + ++$relId; + } + + // Hyperlink on rich text run + if ($iterator2->current() instanceof RichText) { + foreach ($iterator2->current()->getParagraphs() as $paragraph) { + foreach ($paragraph->getRichTextElements() as $element) { + if ($element instanceof Run || $element instanceof TextElement) { + if ($element->hasHyperlink()) { + // Write relationship for hyperlink + $hyperlink = $element->getHyperlink(); + $hyperlink->relationId = 'rId' . $relId; + + if (!$hyperlink->isInternal()) { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External'); + } else { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml'); + } + + ++$relId; + } + } + } + } + } + + // Hyperlink in table + if ($iterator2->current() instanceof ShapeTable) { + // Rows + $countRows = count($iterator2->current()->getRows()); + for ($row = 0; $row < $countRows; $row++) { + // Cells in rows + $countCells = count($iterator2->current()->getRow($row)->getCells()); + for ($cell = 0; $cell < $countCells; $cell++) { + $currentCell = $iterator2->current()->getRow($row)->getCell($cell); + // Paragraphs in cell + foreach ($currentCell->getParagraphs() as $paragraph) { + // RichText in paragraph + foreach ($paragraph->getRichTextElements() as $element) { + // Run or Text in RichText + if ($element instanceof Run || $element instanceof TextElement) { + if ($element->hasHyperlink()) { + // Write relationship for hyperlink + $hyperlink = $element->getHyperlink(); + $hyperlink->relationId = 'rId' . $relId; + + if (!$hyperlink->isInternal()) { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External'); + } else { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml'); + } + + ++$relId; + } + } + } + } + } + } + } + + $iterator2->next(); + } + } + + $iterator->next(); + } + } + + // Write comment relationships + if ($pSlide->getShapeCollection()->count() > 0) { + $hasSlideComment = false; + + // Loop trough images and write relationships + $iterator = $pSlide->getShapeCollection()->getIterator(); + while ($iterator->valid()) { + if ($iterator->current() instanceof Comment) { + $hasSlideComment = true; + break; + } elseif ($iterator->current() instanceof Group) { + $iterator2 = $iterator->current()->getShapeCollection()->getIterator(); + while ($iterator2->valid()) { + if ($iterator2->current() instanceof Comment) { + $hasSlideComment = true; + break 2; + } + $iterator2->next(); + } + } + + $iterator->next(); + } + + if ($hasSlideComment) { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments', '../comments/comment'.($idxSlide + 1).'.xml'); + ++$relId; + } + } + + if ($pSlide->getNote()->getShapeCollection()->count() > 0) { + $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide', '../notesSlides/notesSlide'.($idxSlide + 1).'.xml'); + } + + $objWriter->endElement(); + + // Return + return $objWriter->getData(); + } + + /** + * Write slide to XML format + * + * @param \PhpOffice\PhpPresentation\Slide $pSlide + * @return string XML Output + * @throws \Exception + */ + public function writeSlide(Slide $pSlide) + { + // Create XML writer + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); + + // XML header + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + + // p:sld + $objWriter->startElement('p:sld'); + $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main'); + $objWriter->writeAttributeIf(!$pSlide->isVisible(), 'show', 0); + + // p:sld/p:cSld + $objWriter->startElement('p:cSld'); + + // Background + if ($pSlide->getBackground() instanceof Slide\AbstractBackground) { + $oBackground = $pSlide->getBackground(); + // p:bg + $objWriter->startElement('p:bg'); + + // p:bgPr + $objWriter->startElement('p:bgPr'); + + if ($oBackground instanceof Slide\Background\Color) { + // a:solidFill + $objWriter->startElement('a:solidFill'); + + $this->writeColor($objWriter, $oBackground->getColor()); + + // > a:solidFill + $objWriter->endElement(); + } + + if ($oBackground instanceof Slide\Background\Image) { + // a:blipFill + $objWriter->startElement('a:blipFill'); + + // a:blip + $objWriter->startElement('a:blip'); + $objWriter->writeAttribute('r:embed', $oBackground->relationId); + + // > a:blipFill + $objWriter->endElement(); + + // a:stretch + $objWriter->startElement('a:stretch'); + + // a:fillRect + $objWriter->writeElement('a:fillRect'); + + // > a:stretch + $objWriter->endElement(); + + // > a:blipFill + $objWriter->endElement(); + } + + // > p:bgPr + $objWriter->endElement(); + + // > p:bg + $objWriter->endElement(); + } + + // p:spTree + $objWriter->startElement('p:spTree'); + + // p:nvGrpSpPr + $objWriter->startElement('p:nvGrpSpPr'); + + // p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('id', '1'); + $objWriter->writeAttribute('name', ''); + $objWriter->endElement(); + + // p:cNvGrpSpPr + $objWriter->writeElement('p:cNvGrpSpPr', null); + + // p:nvPr + $objWriter->writeElement('p:nvPr', null); + + $objWriter->endElement(); + + // p:grpSpPr + $objWriter->startElement('p:grpSpPr'); + + // a:xfrm + $objWriter->startElement('a:xfrm'); + + // a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pSlide->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($pSlide->getOffsetY())); + $objWriter->endElement(); // a:off + + // a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($pSlide->getExtentX())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($pSlide->getExtentY())); + $objWriter->endElement(); // a:ext + + // a:chOff + $objWriter->startElement('a:chOff'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pSlide->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($pSlide->getOffsetY())); + $objWriter->endElement(); // a:chOff + + // a:chExt + $objWriter->startElement('a:chExt'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($pSlide->getExtentX())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($pSlide->getExtentY())); + $objWriter->endElement(); // a:chExt + + $objWriter->endElement(); + + $objWriter->endElement(); + + // Loop shapes + $this->writeShapeCollection($objWriter, $pSlide->getShapeCollection()); + + // TODO + $objWriter->endElement(); + + $objWriter->endElement(); + + // p:clrMapOvr + $objWriter->startElement('p:clrMapOvr'); + // p:clrMapOvr\a:masterClrMapping + $objWriter->writeElement('a:masterClrMapping', null); + // ##p:clrMapOvr + $objWriter->endElement(); + + $this->writeSlideTransition($objWriter, $pSlide->getTransition()); + + $this->writeSlideAnimations($objWriter, $pSlide); + + $objWriter->endElement(); + + // Return + return $objWriter->getData(); + } + + /** + * @param XMLWriter $objWriter + * @param Slide $oSlide + */ + protected function writeSlideAnimations(XMLWriter $objWriter, Slide $oSlide) + { + $arrayAnimations = $oSlide->getAnimations(); + if (empty($arrayAnimations)) { + return; + } + + // Variables + $shapeId = 1; + $idCount = 1; + $hashToIdMap = array(); + $arrayAnimationIds = array(); + + foreach ($oSlide->getShapeCollection() as $shape) { + $hashToIdMap[$shape->getHashCode()] = ++$shapeId; + } + foreach ($arrayAnimations as $oAnimation) { + foreach ($oAnimation->getShapeCollection() as $oShape) { + $arrayAnimationIds[] = $hashToIdMap[$oShape->getHashCode()]; + } + } + + // p:timing + $objWriter->startElement('p:timing'); + // p:timing/p:tnLst + $objWriter->startElement('p:tnLst'); + // p:timing/p:tnLst/p:par + $objWriter->startElement('p:par'); + // p:timing/p:tnLst/p:par/p:cTn + $objWriter->startElement('p:cTn'); + $objWriter->writeAttribute('id', $idCount++); + $objWriter->writeAttribute('dur', 'indefinite'); + $objWriter->writeAttribute('restart', 'never'); + $objWriter->writeAttribute('nodeType', 'tmRoot'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst + $objWriter->startElement('p:childTnLst'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq + $objWriter->startElement('p:seq'); + $objWriter->writeAttribute('concurrent', '1'); + $objWriter->writeAttribute('nextAc', 'seek'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn + $objWriter->startElement('p:cTn'); + $objWriter->writeAttribute('id', $idCount++); + $objWriter->writeAttribute('dur', 'indefinite'); + $objWriter->writeAttribute('nodeType', 'mainSeq'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst + $objWriter->startElement('p:childTnLst'); + + // Each animation has multiple shapes + foreach ($arrayAnimations as $oAnimation) { + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par + $objWriter->startElement('p:par'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn + $objWriter->startElement('p:cTn'); + $objWriter->writeAttribute('id', $idCount++); + $objWriter->writeAttribute('fill', 'hold'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:stCondLst + $objWriter->startElement('p:stCondLst'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:stCondLst/p:cond + $objWriter->startElement('p:cond'); + $objWriter->writeAttribute('delay', 'indefinite'); + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn\##p:stCondLst + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst + $objWriter->startElement('p:childTnLst'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par + $objWriter->startElement('p:par'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn + $objWriter->startElement('p:cTn'); + $objWriter->writeAttribute('id', $idCount++); + $objWriter->writeAttribute('fill', 'hold'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn/p:stCondLst + $objWriter->startElement('p:stCondLst'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn/p:stCondLst/p:cond + $objWriter->startElement('p:cond'); + $objWriter->writeAttribute('delay', '0'); + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn\##p:stCondLst + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst + $objWriter->startElement('p:childTnLst'); + + $firstAnimation = true; + foreach ($oAnimation->getShapeCollection() as $oShape) { + $nodeType = $firstAnimation ? 'clickEffect' : 'withEffect'; + $shapeId = $hashToIdMap[$oShape->getHashCode()]; + + // p:par + $objWriter->startElement('p:par'); + // p:par/p:cTn + $objWriter->startElement('p:cTn'); + $objWriter->writeAttribute('id', $idCount++); + $objWriter->writeAttribute('presetID', '1'); + $objWriter->writeAttribute('presetClass', 'entr'); + $objWriter->writeAttribute('fill', 'hold'); + $objWriter->writeAttribute('presetSubtype', '0'); + $objWriter->writeAttribute('grpId', '0'); + $objWriter->writeAttribute('nodeType', $nodeType); + // p:par/p:cTn/p:stCondLst + $objWriter->startElement('p:stCondLst'); + // p:par/p:cTn/p:stCondLst/p:cond + $objWriter->startElement('p:cond'); + $objWriter->writeAttribute('delay', '0'); + $objWriter->endElement(); + // p:par/p:cTn\##p:stCondLst + $objWriter->endElement(); + // p:par/p:cTn/p:childTnLst + $objWriter->startElement('p:childTnLst'); + // p:par/p:cTn/p:childTnLst/p:set + $objWriter->startElement('p:set'); + // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr + $objWriter->startElement('p:cBhvr'); + // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:cTn + $objWriter->startElement('p:cTn'); + $objWriter->writeAttribute('id', $idCount++); + $objWriter->writeAttribute('dur', '1'); + $objWriter->writeAttribute('fill', 'hold'); + // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:cTn/p:stCondLst + $objWriter->startElement('p:stCondLst'); + // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:cTn/p:stCondLst/p:cond + $objWriter->startElement('p:cond'); + $objWriter->writeAttribute('delay', '0'); + $objWriter->endElement(); + // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:cTn\##p:stCondLst + $objWriter->endElement(); + // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr\##p:cTn + $objWriter->endElement(); + // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:tgtEl + $objWriter->startElement('p:tgtEl'); + // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:tgtEl/p:spTgt + $objWriter->startElement('p:spTgt'); + $objWriter->writeAttribute('spid', $shapeId); + $objWriter->endElement(); + // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr\##p:tgtEl + $objWriter->endElement(); + // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:attrNameLst + $objWriter->startElement('p:attrNameLst'); + // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:attrNameLst/p:attrName + $objWriter->writeElement('p:attrName', 'style.visibility'); + // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr\##p:attrNameLst + $objWriter->endElement(); + // p:par/p:cTn/p:childTnLst/p:set\##p:cBhvr + $objWriter->endElement(); + // p:par/p:cTn/p:childTnLst/p:set/p:to + $objWriter->startElement('p:to'); + // p:par/p:cTn/p:childTnLst/p:set/p:to/p:strVal + $objWriter->startElement('p:strVal'); + $objWriter->writeAttribute('val', 'visible'); + $objWriter->endElement(); + // p:par/p:cTn/p:childTnLst/p:set\##p:to + $objWriter->endElement(); + // p:par/p:cTn/p:childTnLst\##p:set + $objWriter->endElement(); + // p:par/p:cTn\##p:childTnLst + $objWriter->endElement(); + // p:par\##p:cTn + $objWriter->endElement(); + // ##p:par + $objWriter->endElement(); + + $firstAnimation = false; + } + + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn\##p:childTnLst + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par\##p:cTn + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst\##p:par + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn\##p:childTnLst + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par\##p:cTn + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst\##p:par + $objWriter->endElement(); + } + + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn\##p:childTnLst + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq\##p:cTn + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:prevCondLst + $objWriter->startElement('p:prevCondLst'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:prevCondLst/p:cond + $objWriter->startElement('p:cond'); + $objWriter->writeAttribute('evt', 'onPrev'); + $objWriter->writeAttribute('delay', '0'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:prevCondLst/p:cond/p:tgtEl + $objWriter->startElement('p:tgtEl'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:prevCondLst/p:cond/p:tgtEl/p:sldTgt + $objWriter->writeElement('p:sldTgt', null); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:prevCondLst/p:cond\##p:tgtEl + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:prevCondLst\##p:cond + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq\##p:prevCondLst + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:nextCondLst + $objWriter->startElement('p:nextCondLst'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:nextCondLst/p:cond + $objWriter->startElement('p:cond'); + $objWriter->writeAttribute('evt', 'onNext'); + $objWriter->writeAttribute('delay', '0'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:nextCondLst/p:cond/p:tgtEl + $objWriter->startElement('p:tgtEl'); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:nextCondLst/p:cond/p:tgtEl/p:sldTgt + $objWriter->writeElement('p:sldTgt', null); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:nextCondLst/p:cond\##p:tgtEl + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:nextCondLst\##p:cond + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq\##p:nextCondLst + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst\##p:seq + $objWriter->endElement(); + // p:timing/p:tnLst/p:par/p:cTn\##p:childTnLst + $objWriter->endElement(); + // p:timing/p:tnLst/p:par\##p:cTn + $objWriter->endElement(); + // p:timing/p:tnLst\##p:par + $objWriter->endElement(); + // p:timing\##p:tnLst + $objWriter->endElement(); + + // p:timing/p:bldLst + $objWriter->startElement('p:bldLst'); + + // Add in ids of all shapes in this slides animations + foreach ($arrayAnimationIds as $id) { + // p:timing/p:bldLst/p:bldP + $objWriter->startElement('p:bldP'); + $objWriter->writeAttribute('spid', $id); + $objWriter->writeAttribute('grpId', 0); + $objWriter->endELement(); + } + + // p:timing\##p:bldLst + $objWriter->endElement(); + + // ##p:timing + $objWriter->endElement(); + } + + /** + * Write group + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Shape\Group $group + * @param int $shapeId + */ + protected function writeShapeGroup(XMLWriter $objWriter, Group $group, &$shapeId) + { + // p:grpSp + $objWriter->startElement('p:grpSp'); + + // p:nvGrpSpPr + $objWriter->startElement('p:nvGrpSpPr'); + + // p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('name', 'Group '.$shapeId++); + $objWriter->writeAttribute('id', $shapeId); + $objWriter->endElement(); // p:cNvPr + // NOTE: Re: $shapeId This seems to be how PowerPoint 2010 does business. + + // p:cNvGrpSpPr + $objWriter->writeElement('p:cNvGrpSpPr', null); + + // p:nvPr + $objWriter->writeElement('p:nvPr', null); + + $objWriter->endElement(); // p:nvGrpSpPr + + // p:grpSpPr + $objWriter->startElement('p:grpSpPr'); + + // a:xfrm + $objWriter->startElement('a:xfrm'); + + // a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($group->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($group->getOffsetY())); + $objWriter->endElement(); // a:off + + // a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($group->getExtentX())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($group->getExtentY())); + $objWriter->endElement(); // a:ext + + // a:chOff + $objWriter->startElement('a:chOff'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($group->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($group->getOffsetY())); + $objWriter->endElement(); // a:chOff + + // a:chExt + $objWriter->startElement('a:chExt'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($group->getExtentX())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($group->getExtentY())); + $objWriter->endElement(); // a:chExt + + $objWriter->endElement(); // a:xfrm + + $objWriter->endElement(); // p:grpSpPr + + $this->writeShapeCollection($objWriter, $group->getShapeCollection(), $shapeId); + $objWriter->endElement(); // p:grpSp + } + + /** + * Write chart + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Shape\Chart $shape + * @param int $shapeId + */ + protected function writeShapeChart(XMLWriter $objWriter, ShapeChart $shape, $shapeId) + { + // p:graphicFrame + $objWriter->startElement('p:graphicFrame'); + + // p:nvGraphicFramePr + $objWriter->startElement('p:nvGraphicFramePr'); + + // p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('id', $shapeId); + $objWriter->writeAttribute('name', $shape->getName()); + $objWriter->writeAttribute('descr', $shape->getDescription()); + $objWriter->endElement(); + + // p:cNvGraphicFramePr + $objWriter->writeElement('p:cNvGraphicFramePr', null); + + // p:nvPr + $objWriter->startElement('p:nvPr'); + if ($shape->isPlaceholder()) { + $objWriter->startElement('p:ph'); + $objWriter->writeAttribute('type', $shape->getPlaceholder()->getType()); + $objWriter->endElement(); + } + $objWriter->endElement(); + + $objWriter->endElement(); + + // p:xfrm + $objWriter->startElement('p:xfrm'); + $objWriter->writeAttributeIf($shape->getRotation() != 0, 'rot', CommonDrawing::degreesToAngle($shape->getRotation())); + + // a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY())); + $objWriter->endElement(); + + // a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight())); + $objWriter->endElement(); + + $objWriter->endElement(); + + // a:graphic + $objWriter->startElement('a:graphic'); + + // a:graphicData + $objWriter->startElement('a:graphicData'); + $objWriter->writeAttribute('uri', 'http://schemas.openxmlformats.org/drawingml/2006/chart'); + + // c:chart + $objWriter->startElement('c:chart'); + $objWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart'); + $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $objWriter->writeAttribute('r:id', $shape->relationId); + $objWriter->endElement(); + + $objWriter->endElement(); + + $objWriter->endElement(); + + $objWriter->endElement(); + } + + /** + * Write pic + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Shape\Drawing\AbstractDrawingAdapter $shape + * @param int $shapeId + * @throws \Exception + */ + protected function writeShapeDrawing(XMLWriter $objWriter, ShapeDrawing\AbstractDrawingAdapter $shape, $shapeId) + { + // p:pic + $objWriter->startElement('p:pic'); + + // p:nvPicPr + $objWriter->startElement('p:nvPicPr'); + + // p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('id', $shapeId); + $objWriter->writeAttribute('name', $shape->getName()); + $objWriter->writeAttribute('descr', $shape->getDescription()); + + // a:hlinkClick + if ($shape->hasHyperlink()) { + $this->writeHyperlink($objWriter, $shape); + } + + $objWriter->endElement(); + + // p:cNvPicPr + $objWriter->startElement('p:cNvPicPr'); + + // a:picLocks + $objWriter->startElement('a:picLocks'); + $objWriter->writeAttribute('noChangeAspect', '1'); + $objWriter->endElement(); + + $objWriter->endElement(); + + // p:nvPr + $objWriter->startElement('p:nvPr'); + // PlaceHolder + if ($shape->isPlaceholder()) { + $objWriter->startElement('p:ph'); + $objWriter->writeAttribute('type', $shape->getPlaceholder()->getType()); + $objWriter->endElement(); + } + /** + * @link : https://github.com/stefslon/exportToPPTX/blob/master/exportToPPTX.m#L2128 + */ + if ($shape instanceof Media) { + // p:nvPr > a:videoFile + $objWriter->startElement('a:videoFile'); + $objWriter->writeAttribute('r:link', $shape->relationId); + $objWriter->endElement(); + // p:nvPr > p:extLst + $objWriter->startElement('p:extLst'); + // p:nvPr > p:extLst > p:ext + $objWriter->startElement('p:ext'); + $objWriter->writeAttribute('uri', '{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}'); + // p:nvPr > p:extLst > p:ext > p14:media + $objWriter->startElement('p14:media'); + $objWriter->writeAttribute('r:embed', $shape->relationId); + $objWriter->writeAttribute('xmlns:p14', 'http://schemas.microsoft.com/office/powerpoint/2010/main'); + // p:nvPr > p:extLst > p:ext > ##p14:media + $objWriter->endElement(); + // p:nvPr > p:extLst > ##p:ext + $objWriter->endElement(); + // p:nvPr > ##p:extLst + $objWriter->endElement(); + } + // ##p:nvPr + $objWriter->endElement(); + $objWriter->endElement(); + + // p:blipFill + $objWriter->startElement('p:blipFill'); + + // a:blip + $objWriter->startElement('a:blip'); + $objWriter->writeAttribute('r:embed', $shape->relationId); + $objWriter->endElement(); + + // a:stretch + $objWriter->startElement('a:stretch'); + $objWriter->writeElement('a:fillRect', null); + $objWriter->endElement(); + + $objWriter->endElement(); + + // p:spPr + $objWriter->startElement('p:spPr'); + // a:xfrm + $objWriter->startElement('a:xfrm'); + $objWriter->writeAttributeIf($shape->getRotation() != 0, 'rot', CommonDrawing::degreesToAngle($shape->getRotation())); + + // a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY())); + $objWriter->endElement(); + + // a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight())); + $objWriter->endElement(); + + $objWriter->endElement(); + + // a:prstGeom + $objWriter->startElement('a:prstGeom'); + $objWriter->writeAttribute('prst', 'rect'); + + // a:avLst + $objWriter->writeElement('a:avLst', null); + + $objWriter->endElement(); + + $this->writeBorder($objWriter, $shape->getBorder(), ''); + + $this->writeShadow($objWriter, $shape->getShadow()); + + $objWriter->endElement(); + + $objWriter->endElement(); + } + + /** + * Write txt + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Shape\RichText $shape + * @param int $shapeId + * @throws \Exception + */ + protected function writeShapeText(XMLWriter $objWriter, RichText $shape, $shapeId) + { + // p:sp + $objWriter->startElement('p:sp'); + + // p:sp/p:nvSpPr + $objWriter->startElement('p:nvSpPr'); + + // p:sp/p:nvSpPr/p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('id', $shapeId); + $objWriter->writeAttribute('name', ''); + + // Hyperlink + if ($shape->hasHyperlink()) { + $this->writeHyperlink($objWriter, $shape); + } + // > p:sp/p:nvSpPr + $objWriter->endElement(); + + // p:sp/p:cNvSpPr + $objWriter->startElement('p:cNvSpPr'); + $objWriter->writeAttribute('txBox', '1'); + $objWriter->endElement(); + // p:sp/p:cNvSpPr/p:nvPr + $objWriter->startElement('p:nvPr'); + if ($shape->isPlaceholder()) { + $objWriter->startElement('p:ph'); + $objWriter->writeAttribute('type', $shape->getPlaceholder()->getType()); + $objWriter->endElement(); + } + $objWriter->endElement(); + + // > p:sp/p:cNvSpPr + $objWriter->endElement(); + + // p:sp/p:spPr + $objWriter->startElement('p:spPr'); + + if (!$shape->isPlaceholder()) { + // p:sp/p:spPr\a:xfrm + $objWriter->startElement('a:xfrm'); + $objWriter->writeAttributeIf($shape->getRotation() != 0, 'rot', CommonDrawing::degreesToAngle($shape->getRotation())); + + // p:sp/p:spPr\a:xfrm\a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY())); + $objWriter->endElement(); + + // p:sp/p:spPr\a:xfrm\a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight())); + $objWriter->endElement(); + + // > p:sp/p:spPr\a:xfrm + $objWriter->endElement(); + + // p:sp/p:spPr\a:prstGeom + $objWriter->startElement('a:prstGeom'); + $objWriter->writeAttribute('prst', 'rect'); + $objWriter->endElement(); + } + + $this->writeFill($objWriter, $shape->getFill()); + $this->writeBorder($objWriter, $shape->getBorder(), ''); + $this->writeShadow($objWriter, $shape->getShadow()); + + // > p:sp/p:spPr + $objWriter->endElement(); + + // p:txBody + $objWriter->startElement('p:txBody'); + + // a:bodyPr + //@link :http://msdn.microsoft.com/en-us/library/documentformat.openxml.drawing.bodyproperties%28v=office.14%29.aspx + $objWriter->startElement('a:bodyPr'); + if (!$shape->isPlaceholder()) { + $verticalAlign = $shape->getActiveParagraph()->getAlignment()->getVertical(); + if ($verticalAlign != Alignment::VERTICAL_BASE && $verticalAlign != Alignment::VERTICAL_AUTO) { + $objWriter->writeAttribute('anchor', $verticalAlign); + } + if ($shape->getWrap() != RichText::WRAP_SQUARE) { + $objWriter->writeAttribute('wrap', $shape->getWrap()); + } + $objWriter->writeAttribute('rtlCol', '0'); + + if ($shape->getHorizontalOverflow() != RichText::OVERFLOW_OVERFLOW) { + $objWriter->writeAttribute('horzOverflow', $shape->getHorizontalOverflow()); + } + if ($shape->getVerticalOverflow() != RichText::OVERFLOW_OVERFLOW) { + $objWriter->writeAttribute('vertOverflow', $shape->getVerticalOverflow()); + } + + if ($shape->isUpright()) { + $objWriter->writeAttribute('upright', '1'); + } + if ($shape->isVertical()) { + $objWriter->writeAttribute('vert', 'vert'); + } + + $objWriter->writeAttribute('bIns', CommonDrawing::pixelsToEmu($shape->getInsetBottom())); + $objWriter->writeAttribute('lIns', CommonDrawing::pixelsToEmu($shape->getInsetLeft())); + $objWriter->writeAttribute('rIns', CommonDrawing::pixelsToEmu($shape->getInsetRight())); + $objWriter->writeAttribute('tIns', CommonDrawing::pixelsToEmu($shape->getInsetTop())); + + if ($shape->getColumns() <> 1) { + $objWriter->writeAttribute('numCol', $shape->getColumns()); + } + + // a:spAutoFit + $objWriter->startElement('a:' . $shape->getAutoFit()); + if ($shape->getAutoFit() == RichText::AUTOFIT_NORMAL) { + if (!is_null($shape->getFontScale())) { + $objWriter->writeAttribute('fontScale', (int)($shape->getFontScale() * 1000)); + } + if (!is_null($shape->getLineSpaceReduction())) { + $objWriter->writeAttribute('lnSpcReduction', (int)($shape->getLineSpaceReduction() * 1000)); + } + } + + $objWriter->endElement(); + } + $objWriter->endElement(); + + // a:lstStyle + $objWriter->startElement('a:lstStyle'); + $objWriter->endElement(); + + // Write paragraphs + $this->writeParagraphs($objWriter, $shape->getParagraphs(), $shape->isPlaceholder()); + + $objWriter->endElement(); + + $objWriter->endElement(); + } + + /** + * Write table + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Shape\Table $shape + * @param int $shapeId + * @throws \Exception + */ + protected function writeShapeTable(XMLWriter $objWriter, ShapeTable $shape, $shapeId) + { + // p:graphicFrame + $objWriter->startElement('p:graphicFrame'); + + // p:nvGraphicFramePr + $objWriter->startElement('p:nvGraphicFramePr'); + + // p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('id', $shapeId); + $objWriter->writeAttribute('name', $shape->getName()); + $objWriter->writeAttribute('descr', $shape->getDescription()); + + $objWriter->endElement(); + + // p:cNvGraphicFramePr + $objWriter->startElement('p:cNvGraphicFramePr'); + + // a:graphicFrameLocks + $objWriter->startElement('a:graphicFrameLocks'); + $objWriter->writeAttribute('noGrp', '1'); + $objWriter->endElement(); + + $objWriter->endElement(); + + // p:nvPr + $objWriter->startElement('p:nvPr'); + if ($shape->isPlaceholder()) { + $objWriter->startElement('p:ph'); + $objWriter->writeAttribute('type', $shape->getPlaceholder()->getType()); + $objWriter->endElement(); + } + $objWriter->endElement(); + + $objWriter->endElement(); + + // p:xfrm + $objWriter->startElement('p:xfrm'); + + // a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY())); + $objWriter->endElement(); + + // a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight())); + $objWriter->endElement(); + + $objWriter->endElement(); + + // a:graphic + $objWriter->startElement('a:graphic'); + + // a:graphicData + $objWriter->startElement('a:graphicData'); + $objWriter->writeAttribute('uri', 'http://schemas.openxmlformats.org/drawingml/2006/table'); + + // a:tbl + $objWriter->startElement('a:tbl'); + + // a:tblPr + $objWriter->startElement('a:tblPr'); + $objWriter->writeAttribute('firstRow', '1'); + $objWriter->writeAttribute('bandRow', '1'); + + $objWriter->endElement(); + + // a:tblGrid + $objWriter->startElement('a:tblGrid'); + + // Write cell widths + $countCells = count($shape->getRow(0)->getCells()); + for ($cell = 0; $cell < $countCells; $cell++) { + // a:gridCol + $objWriter->startElement('a:gridCol'); + + // Calculate column width + $width = $shape->getRow(0)->getCell($cell)->getWidth(); + if ($width == 0) { + $colCount = count($shape->getRow(0)->getCells()); + $totalWidth = $shape->getWidth(); + $width = $totalWidth / $colCount; + } + + $objWriter->writeAttribute('w', CommonDrawing::pixelsToEmu($width)); + $objWriter->endElement(); + } + + $objWriter->endElement(); + + // Colspan / rowspan containers + $colSpan = array(); + $rowSpan = array(); + + // Default border style + $defaultBorder = new Border(); + + // Write rows + $countRows = count($shape->getRows()); + for ($row = 0; $row < $countRows; $row++) { + // a:tr + $objWriter->startElement('a:tr'); + $objWriter->writeAttribute('h', CommonDrawing::pixelsToEmu($shape->getRow($row)->getHeight())); + + // Write cells + $countCells = count($shape->getRow($row)->getCells()); + for ($cell = 0; $cell < $countCells; $cell++) { + // Current cell + $currentCell = $shape->getRow($row)->getCell($cell); + + // Next cell right + $nextCellRight = $shape->getRow($row)->getCell($cell + 1, true); + + // Next cell below + $nextRowBelow = $shape->getRow($row + 1, true); + $nextCellBelow = null; + if ($nextRowBelow != null) { + $nextCellBelow = $nextRowBelow->getCell($cell, true); + } + + // a:tc + $objWriter->startElement('a:tc'); + // Colspan + if ($currentCell->getColSpan() > 1) { + $objWriter->writeAttribute('gridSpan', $currentCell->getColSpan()); + $colSpan[$row] = $currentCell->getColSpan() - 1; + } elseif (isset($colSpan[$row]) && $colSpan[$row] > 0) { + $colSpan[$row]--; + $objWriter->writeAttribute('hMerge', '1'); + } + + // Rowspan + if ($currentCell->getRowSpan() > 1) { + $objWriter->writeAttribute('rowSpan', $currentCell->getRowSpan()); + $rowSpan[$cell] = $currentCell->getRowSpan() - 1; + } elseif (isset($rowSpan[$cell]) && $rowSpan[$cell] > 0) { + $rowSpan[$cell]--; + $objWriter->writeAttribute('vMerge', '1'); + } + + // a:txBody + $objWriter->startElement('a:txBody'); + + // a:bodyPr + $objWriter->startElement('a:bodyPr'); + $objWriter->writeAttribute('wrap', 'square'); + $objWriter->writeAttribute('rtlCol', '0'); + + // a:spAutoFit + $objWriter->writeElement('a:spAutoFit', null); + + $objWriter->endElement(); + + // a:lstStyle + $objWriter->writeElement('a:lstStyle', null); + + // Write paragraphs + $this->writeParagraphs($objWriter, $currentCell->getParagraphs()); + + $objWriter->endElement(); + + // a:tcPr + $objWriter->startElement('a:tcPr'); + // Alignment (horizontal) + $firstParagraph = $currentCell->getParagraph(0); + $verticalAlign = $firstParagraph->getAlignment()->getVertical(); + if ($verticalAlign != Alignment::VERTICAL_BASE && $verticalAlign != Alignment::VERTICAL_AUTO) { + $objWriter->writeAttribute('anchor', $verticalAlign); + } + + // Determine borders + $borderLeft = $currentCell->getBorders()->getLeft(); + $borderRight = $currentCell->getBorders()->getRight(); + $borderTop = $currentCell->getBorders()->getTop(); + $borderBottom = $currentCell->getBorders()->getBottom(); + $borderDiagonalDown = $currentCell->getBorders()->getDiagonalDown(); + $borderDiagonalUp = $currentCell->getBorders()->getDiagonalUp(); + + // Fix PowerPoint implementation + if (!is_null($nextCellRight) && $nextCellRight->getBorders()->getRight()->getHashCode() != $defaultBorder->getHashCode()) { + $borderRight = $nextCellRight->getBorders()->getLeft(); + } + if (!is_null($nextCellBelow) && $nextCellBelow->getBorders()->getBottom()->getHashCode() != $defaultBorder->getHashCode()) { + $borderBottom = $nextCellBelow->getBorders()->getTop(); + } + + // Write borders + $this->writeBorder($objWriter, $borderLeft, 'L'); + $this->writeBorder($objWriter, $borderRight, 'R'); + $this->writeBorder($objWriter, $borderTop, 'T'); + $this->writeBorder($objWriter, $borderBottom, 'B'); + $this->writeBorder($objWriter, $borderDiagonalDown, 'TlToBr'); + $this->writeBorder($objWriter, $borderDiagonalUp, 'BlToTr'); + + // Fill + $this->writeFill($objWriter, $currentCell->getFill()); + + $objWriter->endElement(); + + $objWriter->endElement(); + } + + $objWriter->endElement(); + } + + $objWriter->endElement(); + + $objWriter->endElement(); + + $objWriter->endElement(); + + $objWriter->endElement(); + } + + /** + * Write paragraphs + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Shape\RichText\Paragraph[] $paragraphs + * @param bool $bIsPlaceholder + * @throws \Exception + */ + protected function writeParagraphs(XMLWriter $objWriter, $paragraphs, $bIsPlaceholder = false) + { + // Loop trough paragraphs + foreach ($paragraphs as $paragraph) { + // a:p + $objWriter->startElement('a:p'); + + // a:pPr + if (!$bIsPlaceholder) { + $objWriter->startElement('a:pPr'); + $objWriter->writeAttribute('algn', $paragraph->getAlignment()->getHorizontal()); + $objWriter->writeAttribute('fontAlgn', $paragraph->getAlignment()->getVertical()); + $objWriter->writeAttribute('marL', CommonDrawing::pixelsToEmu($paragraph->getAlignment()->getMarginLeft())); + $objWriter->writeAttribute('marR', CommonDrawing::pixelsToEmu($paragraph->getAlignment()->getMarginRight())); + $objWriter->writeAttribute('indent', CommonDrawing::pixelsToEmu($paragraph->getAlignment()->getIndent())); + $objWriter->writeAttribute('lvl', $paragraph->getAlignment()->getLevel()); + + // Bullet type specified? + if ($paragraph->getBulletStyle()->getBulletType() != Bullet::TYPE_NONE) { + // a:buFont + $objWriter->startElement('a:buFont'); + $objWriter->writeAttribute('typeface', $paragraph->getBulletStyle()->getBulletFont()); + $objWriter->endElement(); + + if ($paragraph->getBulletStyle()->getBulletType() == Bullet::TYPE_BULLET) { + // a:buChar + $objWriter->startElement('a:buChar'); + $objWriter->writeAttribute('char', $paragraph->getBulletStyle()->getBulletChar()); + $objWriter->endElement(); + } elseif ($paragraph->getBulletStyle()->getBulletType() == Bullet::TYPE_NUMERIC) { + // a:buAutoNum + $objWriter->startElement('a:buAutoNum'); + $objWriter->writeAttribute('type', $paragraph->getBulletStyle()->getBulletNumericStyle()); + if ($paragraph->getBulletStyle()->getBulletNumericStartAt() != 1) { + $objWriter->writeAttribute('startAt', $paragraph->getBulletStyle()->getBulletNumericStartAt()); + } + $objWriter->endElement(); + } + if ($paragraph->getBulletStyle()->getBulletColor() instanceof Color) { + $objWriter->startElement('a:buClr'); + $this->writeColor($objWriter, $paragraph->getBulletStyle()->getBulletColor()); + $objWriter->endElement(); + } + } + + $objWriter->startElement('a:lnSpc'); + $objWriter->startElement('a:spcPct'); + $objWriter->writeAttribute('val', $paragraph->getLineSpacing() * 1000); + $objWriter->endElement(); + $objWriter->endElement(); + + $objWriter->endElement(); + } + + // Loop trough rich text elements + $elements = $paragraph->getRichTextElements(); + foreach ($elements as $element) { + if ($element instanceof BreakElement) { + // a:br + $objWriter->writeElement('a:br', null); + } elseif ($element instanceof Run || $element instanceof TextElement) { + // a:r + $objWriter->startElement('a:r'); + + // a:rPr + if ($element instanceof Run && !$bIsPlaceholder) { + // a:rPr + $objWriter->startElement('a:rPr'); + + // Lang + $objWriter->writeAttribute('lang', ($element->getLanguage() ? $element->getLanguage() : 'en-US')); + + $objWriter->writeAttributeIf($element->getFont()->isBold(), 'b', '1'); + $objWriter->writeAttributeIf($element->getFont()->isItalic(), 'i', '1'); + $objWriter->writeAttributeIf($element->getFont()->isStrikethrough(), 'strike', 'sngStrike'); + + // Size + $objWriter->writeAttribute('sz', ($element->getFont()->getSize() * 100)); + + // Underline + $objWriter->writeAttribute('u', $element->getFont()->getUnderline()); + + // Superscript / subscript + $objWriter->writeAttributeIf($element->getFont()->isSuperScript(), 'baseline', '30000'); + $objWriter->writeAttributeIf($element->getFont()->isSubScript(), 'baseline', '-25000'); + + // Color - a:solidFill + $objWriter->startElement('a:solidFill'); + $this->writeColor($objWriter, $element->getFont()->getColor()); + $objWriter->endElement(); + + // Font - a:latin + $objWriter->startElement('a:latin'); + $objWriter->writeAttribute('typeface', $element->getFont()->getName()); + $objWriter->endElement(); + + // a:hlinkClick + $this->writeHyperlink($objWriter, $element); + + $objWriter->endElement(); + } + + // t + $objWriter->startElement('a:t'); + $objWriter->writeCData(Text::controlCharacterPHP2OOXML($element->getText())); + $objWriter->endElement(); + + $objWriter->endElement(); + } + } + + $objWriter->endElement(); + } + } + + /** + * Write Line Shape + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\Shape\Line $shape + * @param int $shapeId + */ + protected function writeShapeLine(XMLWriter $objWriter, Line $shape, $shapeId) + { + // p:sp + $objWriter->startElement('p:cxnSp'); + + // p:nvSpPr + $objWriter->startElement('p:nvCxnSpPr'); + + // p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('id', $shapeId); + $objWriter->writeAttribute('name', ''); + + $objWriter->endElement(); + + // p:cNvCxnSpPr + $objWriter->writeElement('p:cNvCxnSpPr', null); + + // p:nvPr + $objWriter->startElement('p:nvPr'); + if ($shape->isPlaceholder()) { + $objWriter->startElement('p:ph'); + $objWriter->writeAttribute('type', $shape->getPlaceholder()->getType()); + $objWriter->endElement(); + } + $objWriter->endElement(); + + $objWriter->endElement(); + + // p:spPr + $objWriter->startElement('p:spPr'); + + // a:xfrm + $objWriter->startElement('a:xfrm'); + + if ($shape->getWidth() >= 0 && $shape->getHeight() >= 0) { + // a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY())); + $objWriter->endElement(); + + // a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight())); + $objWriter->endElement(); + } elseif ($shape->getWidth() < 0 && $shape->getHeight() < 0) { + // a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX() + $shape->getWidth())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY() + $shape->getHeight())); + $objWriter->endElement(); + + // a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu(-$shape->getWidth())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu(-$shape->getHeight())); + $objWriter->endElement(); + } elseif ($shape->getHeight() < 0) { + $objWriter->writeAttribute('flipV', 1); + + // a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY() + $shape->getHeight())); + $objWriter->endElement(); + + // a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu(-$shape->getHeight())); + $objWriter->endElement(); + } elseif ($shape->getWidth() < 0) { + $objWriter->writeAttribute('flipV', 1); + + // a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX() + $shape->getWidth())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY())); + $objWriter->endElement(); + + // a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu(-$shape->getWidth())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight())); + $objWriter->endElement(); + } + + $objWriter->endElement(); + + // a:prstGeom + $objWriter->startElement('a:prstGeom'); + $objWriter->writeAttribute('prst', 'line'); + $objWriter->endElement(); + + if ($shape->getBorder()->getLineStyle() != Border::LINE_NONE) { + $this->writeBorder($objWriter, $shape->getBorder(), ''); + } + + $objWriter->endElement(); + + $objWriter->endElement(); + } + + /** + * Write hyperlink + * + * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer + * @param \PhpOffice\PhpPresentation\AbstractShape|\PhpOffice\PhpPresentation\Shape\RichText\TextElement $shape + */ + protected function writeHyperlink(XMLWriter $objWriter, $shape) + { + if (!$shape->hasHyperlink()) { + return; + } + // a:hlinkClick + $objWriter->startElement('a:hlinkClick'); + $objWriter->writeAttribute('r:id', $shape->getHyperlink()->relationId); + $objWriter->writeAttribute('tooltip', $shape->getHyperlink()->getTooltip()); + if ($shape->getHyperlink()->isInternal()) { + $objWriter->writeAttribute('action', $shape->getHyperlink()->getUrl()); + } + $objWriter->endElement(); + } + + /** + * Write Note Slide + * @param Note $pNote + * @throws \Exception + * @return string + */ + protected function writeNote(Note $pNote) + { + // Create XML writer + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); + + // XML header + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + + // p:notes + $objWriter->startElement('p:notes'); + $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main'); + $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + + // p:notes/p:cSld + $objWriter->startElement('p:cSld'); + + // p:notes/p:cSld/p:spTree + $objWriter->startElement('p:spTree'); + + // p:notes/p:cSld/p:spTree/p:nvGrpSpPr + $objWriter->startElement('p:nvGrpSpPr'); + + // p:notes/p:cSld/p:spTree/p:nvGrpSpPr/p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('id', '1'); + $objWriter->writeAttribute('name', ''); + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:nvGrpSpPr/p:cNvGrpSpPr + $objWriter->writeElement('p:cNvGrpSpPr', null); + + // p:notes/p:cSld/p:spTree/p:nvGrpSpPr/p:nvPr + $objWriter->writeElement('p:nvPr', null); + + // p:notes/p:cSld/p:spTree/p:nvGrpSpPr + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:grpSpPr + $objWriter->startElement('p:grpSpPr'); + + // p:notes/p:cSld/p:spTree/p:grpSpPr/a:xfrm + $objWriter->startElement('a:xfrm'); + + // p:notes/p:cSld/p:spTree/p:grpSpPr/a:xfrm/a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pNote->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($pNote->getOffsetY())); + $objWriter->endElement(); // a:off + + // p:notes/p:cSld/p:spTree/p:grpSpPr/a:xfrm/a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($pNote->getExtentX())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($pNote->getExtentY())); + $objWriter->endElement(); // a:ext + + // p:notes/p:cSld/p:spTree/p:grpSpPr/a:xfrm/a:chOff + $objWriter->startElement('a:chOff'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pNote->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($pNote->getOffsetY())); + $objWriter->endElement(); // a:chOff + + // p:notes/p:cSld/p:spTree/p:grpSpPr/a:xfrm/a:chExt + $objWriter->startElement('a:chExt'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($pNote->getExtentX())); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($pNote->getExtentY())); + $objWriter->endElement(); // a:chExt + + // p:notes/p:cSld/p:spTree/p:grpSpPr/a:xfrm + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:grpSpPr + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1] + $objWriter->startElement('p:sp'); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr + $objWriter->startElement('p:nvSpPr'); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('id', '2'); + $objWriter->writeAttribute('name', 'Slide Image Placeholder 1'); + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:cNvSpPr + $objWriter->startElement('p:cNvSpPr'); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:cNvSpPr/a:spLocks + $objWriter->startElement('a:spLocks'); + $objWriter->writeAttribute('noGrp', '1'); + $objWriter->writeAttribute('noRot', '1'); + $objWriter->writeAttribute('noChangeAspect', '1'); + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:cNvSpPr + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:nvPr + $objWriter->startElement('p:nvPr'); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:nvPr/p:ph + $objWriter->startElement('p:ph'); + $objWriter->writeAttribute('type', 'sldImg'); + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:nvPr + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr + $objWriter->startElement('p:spPr'); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:xfrm + $objWriter->startElement('a:xfrm'); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:xfrm/a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', 0); + $objWriter->writeAttribute('y', 0); + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:xfrm/a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu(round($pNote->getExtentX() / 2))); + $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu(round($pNote->getExtentY() / 2))); + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:xfrm + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:prstGeom + $objWriter->startElement('a:prstGeom'); + $objWriter->writeAttribute('prst', 'rect'); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:prstGeom/a:avLst + $objWriter->writeElement('a:avLst', null); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:prstGeom + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:noFill + $objWriter->writeElement('a:noFill', null); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:ln + $objWriter->startElement('a:ln'); + $objWriter->writeAttribute('w', '12700'); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:ln/a:solidFill + $objWriter->startElement('a:solidFill'); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:ln/a:solidFill/a:prstClr + $objWriter->startElement('a:prstClr'); + $objWriter->writeAttribute('val', 'black'); + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:ln/a:solidFill + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:ln + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[1] + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[2] + $objWriter->startElement('p:sp'); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr + $objWriter->startElement('p:nvSpPr'); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:cNvPr + $objWriter->startElement('p:cNvPr'); + $objWriter->writeAttribute('id', '3'); + $objWriter->writeAttribute('name', 'Notes Placeholder'); + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:cNvSpPr + $objWriter->startElement('p:cNvSpPr'); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:cNvSpPr/a:spLocks + $objWriter->startElement('a:spLocks'); + $objWriter->writeAttribute('noGrp', '1'); + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:cNvSpPr + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:nvPr + $objWriter->startElement('p:nvPr'); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:nvPr/p:ph + $objWriter->startElement('p:ph'); + $objWriter->writeAttribute('type', 'body'); + $objWriter->writeAttribute('idx', '1'); + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:nvPr + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr + $objWriter->endElement(); + + // START notes print below rectangle section + // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr + $objWriter->startElement('p:spPr'); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:xfrm + $objWriter->startElement('a:xfrm'); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:xfrm/a:off + $objWriter->startElement('a:off'); + $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pNote->getOffsetX())); + $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu(round($pNote->getExtentY() / 2) + $pNote->getOffsetY())); + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:xfrm/a:ext + $objWriter->startElement('a:ext'); + $objWriter->writeAttribute('cx', '5486400'); + $objWriter->writeAttribute('cy', '3600450'); + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:xfrm + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:prstGeom + $objWriter->startElement('a:prstGeom'); + $objWriter->writeAttribute('prst', 'rect'); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:prstGeom/a:avLst + $objWriter->writeElement('a:avLst', null); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:prstGeom + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:txBody + $objWriter->startElement('p:txBody'); + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:txBody/a:bodyPr + $objWriter->writeElement('a:bodyPr', null); + // p:notes/p:cSld/p:spTree/p:sp[2]/p:txBody/a:lstStyle + $objWriter->writeElement('a:lstStyle', null); + + // Loop shapes + $shapes = $pNote->getShapeCollection(); + foreach ($shapes as $shape) { + // Check type + if ($shape instanceof RichText) { + $paragraphs = $shape->getParagraphs(); + $this->writeParagraphs($objWriter, $paragraphs); + } + } + + // p:notes/p:cSld/p:spTree/p:sp[2]/p:txBody + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree/p:sp[2] + $objWriter->endElement(); + + // p:notes/p:cSld/p:spTree + $objWriter->endElement(); + + // p:notes/p:cSld + $objWriter->endElement(); + + // p:notes + $objWriter->endElement(); + + // Return + return $objWriter->getData(); + } + + /** + * Write Transition Slide + * @link http://officeopenxml.com/prSlide-transitions.php + * @param XMLWriter $objWriter + * @param Transition $transition + */ + protected function writeSlideTransition(XMLWriter $objWriter, $transition) + { + if (!$transition instanceof Transition) { + return; + } + $objWriter->startElement('p:transition'); + if (!is_null($transition->getSpeed())) { + $objWriter->writeAttribute('spd', $transition->getSpeed()); + } + $objWriter->writeAttribute('advClick', $transition->hasManualTrigger() ? '1' : '0'); + if ($transition->hasTimeTrigger()) { + $objWriter->writeAttribute('advTm', $transition->getAdvanceTimeTrigger()); + } + + switch ($transition->getTransitionType()) { + case Transition::TRANSITION_BLINDS_HORIZONTAL: + $objWriter->startElement('p:blinds'); + $objWriter->writeAttribute('dir', 'horz'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_BLINDS_VERTICAL: + $objWriter->startElement('p:blinds'); + $objWriter->writeAttribute('dir', 'vert'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_CHECKER_HORIZONTAL: + $objWriter->startElement('p:checker'); + $objWriter->writeAttribute('dir', 'horz'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_CHECKER_VERTICAL: + $objWriter->startElement('p:checker'); + $objWriter->writeAttribute('dir', 'vert'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_CIRCLE_HORIZONTAL: + $objWriter->startElement('p:circle'); + $objWriter->writeAttribute('dir', 'horz'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_CIRCLE_VERTICAL: + $objWriter->startElement('p:circle'); + $objWriter->writeAttribute('dir', 'vert'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_COMB_HORIZONTAL: + $objWriter->startElement('p:comb'); + $objWriter->writeAttribute('dir', 'horz'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_COMB_VERTICAL: + $objWriter->startElement('p:comb'); + $objWriter->writeAttribute('dir', 'vert'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_COVER_DOWN: + $objWriter->startElement('p:cover'); + $objWriter->writeAttribute('dir', 'd'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_COVER_LEFT: + $objWriter->startElement('p:cover'); + $objWriter->writeAttribute('dir', 'l'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_COVER_LEFT_DOWN: + $objWriter->startElement('p:cover'); + $objWriter->writeAttribute('dir', 'ld'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_COVER_LEFT_UP: + $objWriter->startElement('p:cover'); + $objWriter->writeAttribute('dir', 'lu'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_COVER_RIGHT: + $objWriter->startElement('p:cover'); + $objWriter->writeAttribute('dir', 'r'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_COVER_RIGHT_DOWN: + $objWriter->startElement('p:cover'); + $objWriter->writeAttribute('dir', 'rd'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_COVER_RIGHT_UP: + $objWriter->startElement('p:cover'); + $objWriter->writeAttribute('dir', 'ru'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_COVER_UP: + $objWriter->startElement('p:cover'); + $objWriter->writeAttribute('dir', 'u'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_CUT: + $objWriter->writeElement('p:cut'); + break; + case Transition::TRANSITION_DIAMOND: + $objWriter->writeElement('p:diamond'); + break; + case Transition::TRANSITION_DISSOLVE: + $objWriter->writeElement('p:dissolve'); + break; + case Transition::TRANSITION_FADE: + $objWriter->writeElement('p:fade'); + break; + case Transition::TRANSITION_NEWSFLASH: + $objWriter->writeElement('p:newsflash'); + break; + case Transition::TRANSITION_PLUS: + $objWriter->writeElement('p:plus'); + break; + case Transition::TRANSITION_PULL_DOWN: + $objWriter->startElement('p:pull'); + $objWriter->writeAttribute('dir', 'd'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_PULL_LEFT: + $objWriter->startElement('p:pull'); + $objWriter->writeAttribute('dir', 'l'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_PULL_RIGHT: + $objWriter->startElement('p:pull'); + $objWriter->writeAttribute('dir', 'r'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_PULL_UP: + $objWriter->startElement('p:pull'); + $objWriter->writeAttribute('dir', 'u'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_PUSH_DOWN: + $objWriter->startElement('p:push'); + $objWriter->writeAttribute('dir', 'd'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_PUSH_LEFT: + $objWriter->startElement('p:push'); + $objWriter->writeAttribute('dir', 'l'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_PUSH_RIGHT: + $objWriter->startElement('p:push'); + $objWriter->writeAttribute('dir', 'r'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_PUSH_UP: + $objWriter->startElement('p:push'); + $objWriter->writeAttribute('dir', 'u'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_RANDOM: + $objWriter->writeElement('p:random'); + break; + case Transition::TRANSITION_RANDOMBAR_HORIZONTAL: + $objWriter->startElement('p:randomBar'); + $objWriter->writeAttribute('dir', 'horz'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_RANDOMBAR_VERTICAL: + $objWriter->startElement('p:randomBar'); + $objWriter->writeAttribute('dir', 'vert'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_SPLIT_IN_HORIZONTAL: + $objWriter->startElement('p:split'); + $objWriter->writeAttribute('dir', 'in'); + $objWriter->writeAttribute('orient', 'horz'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_SPLIT_OUT_HORIZONTAL: + $objWriter->startElement('p:split'); + $objWriter->writeAttribute('dir', 'out'); + $objWriter->writeAttribute('orient', 'horz'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_SPLIT_IN_VERTICAL: + $objWriter->startElement('p:split'); + $objWriter->writeAttribute('dir', 'in'); + $objWriter->writeAttribute('orient', 'vert'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_SPLIT_OUT_VERTICAL: + $objWriter->startElement('p:split'); + $objWriter->writeAttribute('dir', 'out'); + $objWriter->writeAttribute('orient', 'vert'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_STRIPS_LEFT_DOWN: + $objWriter->startElement('p:strips'); + $objWriter->writeAttribute('dir', 'ld'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_STRIPS_LEFT_UP: + $objWriter->startElement('p:strips'); + $objWriter->writeAttribute('dir', 'lu'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_STRIPS_RIGHT_DOWN: + $objWriter->startElement('p:strips'); + $objWriter->writeAttribute('dir', 'rd'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_STRIPS_RIGHT_UP: + $objWriter->startElement('p:strips'); + $objWriter->writeAttribute('dir', 'ru'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_WEDGE: + $objWriter->writeElement('p:wedge'); + break; + case Transition::TRANSITION_WIPE_DOWN: + $objWriter->startElement('p:wipe'); + $objWriter->writeAttribute('dir', 'd'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_WIPE_LEFT: + $objWriter->startElement('p:wipe'); + $objWriter->writeAttribute('dir', 'l'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_WIPE_RIGHT: + $objWriter->startElement('p:wipe'); + $objWriter->writeAttribute('dir', 'r'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_WIPE_UP: + $objWriter->startElement('p:wipe'); + $objWriter->writeAttribute('dir', 'u'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_ZOOM_IN: + $objWriter->startElement('p:zoom'); + $objWriter->writeAttribute('dir', 'in'); + $objWriter->endElement(); + break; + case Transition::TRANSITION_ZOOM_OUT: + $objWriter->startElement('p:zoom'); + $objWriter->writeAttribute('dir', 'out'); + $objWriter->endElement(); + break; + } + + $objWriter->endElement(); + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/PptTableProps.php b/src/PhpPresentation/Writer/PowerPoint2007/PptTableProps.php new file mode 100644 index 000000000..f79a1a15a --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/PptTableProps.php @@ -0,0 +1,29 @@ +startDocument('1.0', 'UTF-8', 'yes'); + + // a:tblStyleLst + $objWriter->startElement('a:tblStyleLst'); + $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + $objWriter->writeAttribute('def', '{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}'); + $objWriter->endElement(); + + $this->getZip()->addFromString('ppt/tableStyles.xml', $objWriter->getData()); + + return $this->getZip(); + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/PptTheme.php b/src/PhpPresentation/Writer/PowerPoint2007/PptTheme.php new file mode 100644 index 000000000..68082f30e --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/PptTheme.php @@ -0,0 +1,857 @@ +oPresentation->getAllMasterSlides() as $oMasterSlide) { + $this->getZip()->addFromString('ppt/theme/theme' . $oMasterSlide->getRelsIndex() . '.xml', $this->writeTheme($oMasterSlide)); + } + + return $this->getZip(); + } + + + /** + * Write theme to XML format + * + * @param Slide\SlideMaster $oMasterSlide + * @return string XML Output + */ + protected function writeTheme(Slide\SlideMaster $oMasterSlide) + { + $arrayFont = array( + 'Jpan' => 'MS Pゴシック', + 'Hang' => '맑은 고딕', + 'Hans' => '宋体', + 'Hant' => '新細明體', + 'Arab' => 'Times New Roman', + 'Hebr' => 'Times New Roman', + 'Thai' => 'Angsana New', + 'Ethi' => 'Nyala', + 'Beng' => 'Vrinda', + 'Gujr' => 'Shruti', + 'Khmr' => 'MoolBoran', + 'Knda' => 'Tunga', + 'Guru' => 'Raavi', + 'Cans' => 'Euphemia', + 'Cher' => 'Plantagenet Cherokee', + 'Yiii' => 'Microsoft Yi Baiti', + 'Tibt' => 'Microsoft Himalaya', + 'Thaa' => 'MV Boli', + 'Deva' => 'Mangal', + 'Telu' => 'Gautami', + 'Taml' => 'Latha', + 'Syrc' => 'Estrangelo Edessa', + 'Orya' => 'Kalinga', + 'Mlym' => 'Kartika', + 'Laoo' => 'DokChampa', + 'Sinh' => 'Iskoola Pota', + 'Mong' => 'Mongolian Baiti', + 'Viet' => 'Times New Roman', + 'Uigh' => 'Microsoft Uighur', + ); + + // Create XML writer + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); + + $name = 'Theme'.rand(1, 100); + + // XML header + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + + // a:theme + $objWriter->startElement('a:theme'); + $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + $objWriter->writeAttribute('name', $name); + + // a:theme/a:themeElements + $objWriter->startElement('a:themeElements'); + + // a:theme/a:themeElements/a:clrScheme + $objWriter->startElement('a:clrScheme'); + $objWriter->writeAttribute('name', $name); + + foreach ($oMasterSlide->getAllSchemeColors() as $oSchemeColor) { + // a:theme/a:themeElements/a:clrScheme/a:* + $objWriter->startElement('a:'.$oSchemeColor->getValue()); + + if (in_array($oSchemeColor->getValue(), array( + 'dk1', 'lt1' + ))) { + $objWriter->startElement('a:sysClr'); + $objWriter->writeAttribute('val', ($oSchemeColor->getValue() == 'dk1' ? 'windowText' : 'window')); + $objWriter->writeAttribute('lastClr', $oSchemeColor->getRGB()); + $objWriter->endElement(); + } else { + $objWriter->startElement('a:srgbClr'); + $objWriter->writeAttribute('val', $oSchemeColor->getRGB()); + $objWriter->endElement(); + } + + // a:theme/a:themeElements/a:clrScheme/a:*/ + $objWriter->endElement(); + } + + // a:theme/a:themeElements/a:clrScheme/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fontScheme + $objWriter->startElement('a:fontScheme'); + $objWriter->writeAttribute('name', $name); + + // a:theme/a:themeElements/a:fontScheme/a:majorFont + $objWriter->startElement('a:majorFont'); + + // a:theme/a:themeElements/a:fontScheme/a:majorFont/a:latin + $objWriter->startElement('a:latin'); + $objWriter->writeAttribute('typeface', 'Calibri'); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fontScheme/a:majorFont/a:ea + $objWriter->startElement('a:ea'); + $objWriter->writeAttribute('typeface', ''); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fontScheme/a:majorFont/a:cs + $objWriter->startElement('a:cs'); + $objWriter->writeAttribute('typeface', ''); + $objWriter->endElement(); + + foreach ($arrayFont as $script => $typeface) { + // a:theme/a:themeElements/a:fontScheme/a:majorFont/a:font + $objWriter->startElement('a:font'); + $objWriter->writeAttribute('script', $script); + $objWriter->writeAttribute('typeface', $typeface); + $objWriter->endElement(); + } + + // a:theme/a:themeElements/a:fontScheme/a:majorFont/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fontScheme/a:minorFont + $objWriter->startElement('a:minorFont'); + + // a:theme/a:themeElements/a:fontScheme/a:majorFont/a:latin + $objWriter->startElement('a:latin'); + $objWriter->writeAttribute('typeface', 'Calibri'); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fontScheme/a:majorFont/a:ea + $objWriter->startElement('a:ea'); + $objWriter->writeAttribute('typeface', ''); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fontScheme/a:majorFont/a:cs + $objWriter->startElement('a:cs'); + $objWriter->writeAttribute('typeface', ''); + $objWriter->endElement(); + + foreach ($arrayFont as $script => $typeface) { + // a:theme/a:themeElements/a:fontScheme/a:majorFont/a:font + $objWriter->startElement('a:font'); + $objWriter->writeAttribute('script', $script); + $objWriter->writeAttribute('typeface', $typeface); + $objWriter->endElement(); + } + + // a:theme/a:themeElements/a:fontScheme/a:minorFont/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fontScheme/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme + $objWriter->startElement('a:fmtScheme'); + $objWriter->writeAttribute('name', $name); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst + $objWriter->startElement('a:fillStyleLst'); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:solidFill + $objWriter->startElement('a:solidFill'); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:solidFill/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:solidFill/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:solidFill/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill + $objWriter->startElement('a:gradFill'); + $objWriter->writeAttribute('rotWithShape', 1); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst + $objWriter->startElement('a:gsLst'); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs + $objWriter->startElement('a:gs'); + $objWriter->writeAttribute('pos', 0); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:tint + $objWriter->startElement('a:tint'); + $objWriter->writeAttribute('val', 50000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:satMod + $objWriter->startElement('a:satMod'); + $objWriter->writeAttribute('val', 300000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs + $objWriter->startElement('a:gs'); + $objWriter->writeAttribute('pos', 35000); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:tint + $objWriter->startElement('a:tint'); + $objWriter->writeAttribute('val', 37000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:satMod + $objWriter->startElement('a:satMod'); + $objWriter->writeAttribute('val', 300000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs + $objWriter->startElement('a:gs'); + $objWriter->writeAttribute('pos', 100000); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:tint + $objWriter->startElement('a:tint'); + $objWriter->writeAttribute('val', 15000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:satMod + $objWriter->startElement('a:satMod'); + $objWriter->writeAttribute('val', 350000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:lin + $objWriter->startElement('a:lin'); + $objWriter->writeAttribute('ang', 16200000); + $objWriter->writeAttribute('scaled', 1); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill + $objWriter->startElement('a:gradFill'); + $objWriter->writeAttribute('rotWithShape', 1); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst + $objWriter->startElement('a:gsLst'); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs + $objWriter->startElement('a:gs'); + $objWriter->writeAttribute('pos', 0); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:shade + $objWriter->startElement('a:shade'); + $objWriter->writeAttribute('val', 51000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:satMod + $objWriter->startElement('a:satMod'); + $objWriter->writeAttribute('val', 130000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs + $objWriter->startElement('a:gs'); + $objWriter->writeAttribute('pos', 80000); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:shade + $objWriter->startElement('a:shade'); + $objWriter->writeAttribute('val', 93000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:satMod + $objWriter->startElement('a:satMod'); + $objWriter->writeAttribute('val', 130000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs + $objWriter->startElement('a:gs'); + $objWriter->writeAttribute('pos', 100000); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:shade + $objWriter->startElement('a:shade'); + $objWriter->writeAttribute('val', 94000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:satMod + $objWriter->startElement('a:satMod'); + $objWriter->writeAttribute('val', 135000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:lin + $objWriter->startElement('a:lin'); + $objWriter->writeAttribute('ang', 16200000); + $objWriter->writeAttribute('scaled', 0); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst + $objWriter->startElement('a:lnStyleLst'); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln + $objWriter->startElement('a:ln'); + $objWriter->writeAttribute('w', 9525); + $objWriter->writeAttribute('cap', 'flat'); + $objWriter->writeAttribute('cmpd', 'sng'); + $objWriter->writeAttribute('algn', 'ctr'); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:solidFill + $objWriter->startElement('a:solidFill'); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:solidFill/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:shade + $objWriter->startElement('a:shade'); + $objWriter->writeAttribute('val', 95000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:fillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:satMod + $objWriter->startElement('a:satMod'); + $objWriter->writeAttribute('val', 105000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:solidFill/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:solidFill/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:prstDash + $objWriter->startElement('a:prstDash'); + $objWriter->writeAttribute('val', 'solid'); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln + $objWriter->startElement('a:ln'); + $objWriter->writeAttribute('w', 25400); + $objWriter->writeAttribute('cap', 'flat'); + $objWriter->writeAttribute('cmpd', 'sng'); + $objWriter->writeAttribute('algn', 'ctr'); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:solidFill + $objWriter->startElement('a:solidFill'); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:solidFill/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:solidFill/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:solidFill/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:prstDash + $objWriter->startElement('a:prstDash'); + $objWriter->writeAttribute('val', 'solid'); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln + $objWriter->startElement('a:ln'); + $objWriter->writeAttribute('w', 38100); + $objWriter->writeAttribute('cap', 'flat'); + $objWriter->writeAttribute('cmpd', 'sng'); + $objWriter->writeAttribute('algn', 'ctr'); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:solidFill + $objWriter->startElement('a:solidFill'); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:solidFill/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:solidFill/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:solidFill/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/a:prstDash + $objWriter->startElement('a:prstDash'); + $objWriter->writeAttribute('val', 'solid'); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/a:ln/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:lnStyleLst/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst + $objWriter->startElement('a:effectStyleLst'); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle + $objWriter->startElement('a:effectStyle'); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst + $objWriter->startElement('a:effectLst'); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw + $objWriter->startElement('a:outerShdw'); + $objWriter->writeAttribute('blurRad', 40000); + $objWriter->writeAttribute('dir', 5400000); + $objWriter->writeAttribute('dist', 20000); + $objWriter->writeAttribute('rotWithShape', 0); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/a:srgbClr + $objWriter->startElement('a:srgbClr'); + $objWriter->writeAttribute('val', '000000'); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/a:srgbClr/a:alpha + $objWriter->startElement('a:alpha'); + $objWriter->writeAttribute('val', 38000); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/a:srgbClr/a:alpha/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/a:srgbClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle + $objWriter->startElement('a:effectStyle'); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst + $objWriter->startElement('a:effectLst'); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw + $objWriter->startElement('a:outerShdw'); + $objWriter->writeAttribute('blurRad', 40000); + $objWriter->writeAttribute('dir', 5400000); + $objWriter->writeAttribute('dist', 23000); + $objWriter->writeAttribute('rotWithShape', 0); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/a:srgbClr + $objWriter->startElement('a:srgbClr'); + $objWriter->writeAttribute('val', '000000'); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/a:srgbClr/a:alpha + $objWriter->startElement('a:alpha'); + $objWriter->writeAttribute('val', 35000); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/a:srgbClr/a:alpha/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/a:srgbClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle + $objWriter->startElement('a:effectStyle'); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst + $objWriter->startElement('a:effectLst'); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw + $objWriter->startElement('a:outerShdw'); + $objWriter->writeAttribute('blurRad', 40000); + $objWriter->writeAttribute('dir', 5400000); + $objWriter->writeAttribute('dist', 23000); + $objWriter->writeAttribute('rotWithShape', 0); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/a:srgbClr + $objWriter->startElement('a:srgbClr'); + $objWriter->writeAttribute('val', '000000'); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/a:srgbClr/a:alpha + $objWriter->startElement('a:alpha'); + $objWriter->writeAttribute('val', 35000); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/a:srgbClr/a:alpha/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/a:srgbClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/a:outerShdw/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:effectLst/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:scene3d + $objWriter->startElement('a:scene3d'); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:scene3d/a:camera + $objWriter->startElement('a:camera'); + $objWriter->writeAttribute('prst', 'orthographicFront'); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:scene3d/a:camera/a:rot + $objWriter->startElement('a:rot'); + $objWriter->writeAttribute('lat', 0); + $objWriter->writeAttribute('lon', 0); + $objWriter->writeAttribute('rev', 0); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:scene3d/a:camera/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:scene3d/a:lightRig + $objWriter->startElement('a:lightRig'); + $objWriter->writeAttribute('dir', 't'); + $objWriter->writeAttribute('rig', 'threePt'); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:scene3d/a:lightRig/a:rot + $objWriter->startElement('a:rot'); + $objWriter->writeAttribute('lat', 0); + $objWriter->writeAttribute('lon', 0); + $objWriter->writeAttribute('rev', 1200000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:scene3d/a:lightRig/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:scene3d/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:sp3d + $objWriter->startElement('a:sp3d'); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:sp3d/a:bevelT + $objWriter->startElement('a:bevelT'); + $objWriter->writeAttribute('h', 25400); + $objWriter->writeAttribute('w', 63500); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/a:sp3d/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/a:effectStyle/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst + $objWriter->startElement('a:bgFillStyleLst'); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:solidFill + $objWriter->startElement('a:solidFill'); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:solidFill/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:solidFill/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:solidFill/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill + $objWriter->startElement('a:gradFill'); + $objWriter->writeAttribute('rotWithShape', 1); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst + $objWriter->startElement('a:gsLst'); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs + $objWriter->startElement('a:gs'); + $objWriter->writeAttribute('pos', 0); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:tint + $objWriter->startElement('a:tint'); + $objWriter->writeAttribute('val', 40000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:satMod + $objWriter->startElement('a:satMod'); + $objWriter->writeAttribute('val', 350000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs + $objWriter->startElement('a:gs'); + $objWriter->writeAttribute('pos', 40000); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:shade + $objWriter->startElement('a:tint'); + $objWriter->writeAttribute('val', 45000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:shade + $objWriter->startElement('a:shade'); + $objWriter->writeAttribute('val', 99000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:satMod + $objWriter->startElement('a:satMod'); + $objWriter->writeAttribute('val', 350000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs + $objWriter->startElement('a:gs'); + $objWriter->writeAttribute('pos', 100000); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:shade + $objWriter->startElement('a:shade'); + $objWriter->writeAttribute('val', 20000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:satMod + $objWriter->startElement('a:satMod'); + $objWriter->writeAttribute('val', 255000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:path + $objWriter->startElement('a:path'); + $objWriter->writeAttribute('path', 'circle'); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:path/a:fillToRect + $objWriter->startElement('a:fillToRect'); + $objWriter->writeAttribute('b', 180000); + $objWriter->writeAttribute('l', 50000); + $objWriter->writeAttribute('r', 50000); + $objWriter->writeAttribute('t', -80000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:path/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill + $objWriter->startElement('a:gradFill'); + $objWriter->writeAttribute('rotWithShape', 1); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst + $objWriter->startElement('a:gsLst'); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs + $objWriter->startElement('a:gs'); + $objWriter->writeAttribute('pos', 0); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:tint + $objWriter->startElement('a:tint'); + $objWriter->writeAttribute('val', 80000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:satMod + $objWriter->startElement('a:satMod'); + $objWriter->writeAttribute('val', 300000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs + $objWriter->startElement('a:gs'); + $objWriter->writeAttribute('pos', 100000); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', 'phClr'); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:shade + $objWriter->startElement('a:shade'); + $objWriter->writeAttribute('val', 30000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/a:satMod + $objWriter->startElement('a:satMod'); + $objWriter->writeAttribute('val', 200000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/a:schemeClr/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/a:gs/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:gsLst/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:path + $objWriter->startElement('a:path'); + $objWriter->writeAttribute('path', 'circle'); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:path/a:fillToRect + $objWriter->startElement('a:fillToRect'); + $objWriter->writeAttribute('b', 50000); + $objWriter->writeAttribute('l', 50000); + $objWriter->writeAttribute('r', 50000); + $objWriter->writeAttribute('t', 50000); + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/a:path/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/ + $objWriter->endElement(); + + // a:theme/a:themeElements/a:fmtScheme/ + $objWriter->endElement(); + + // a:theme/a:themeElements/ + $objWriter->endElement(); + + // a:theme/a:themeElements + $objWriter->writeElement('a:objectDefaults'); + + // a:theme/a:extraClrSchemeLst + $objWriter->writeElement('a:extraClrSchemeLst'); + + // a:theme/ + $objWriter->endElement(); + + // Return + return $objWriter->getData(); + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/PptViewProps.php b/src/PhpPresentation/Writer/PowerPoint2007/PptViewProps.php new file mode 100644 index 000000000..2c155d5c9 --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/PptViewProps.php @@ -0,0 +1,73 @@ +startDocument('1.0', 'UTF-8', 'yes'); + + // p:viewPr + $objWriter->startElement('p:viewPr'); + $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main'); + $objWriter->writeAttribute('showComments', $this->getPresentation()->getPresentationProperties()->isCommentVisible() ? 1 : 0); + $objWriter->writeAttribute('lastView', $this->getPresentation()->getPresentationProperties()->getLastView()); + + // p:viewPr > p:slideViewPr + $objWriter->startElement('p:slideViewPr'); + + // p:viewPr > p:slideViewPr > p:cSldViewPr + $objWriter->startElement('p:cSldViewPr'); + + // p:viewPr > p:slideViewPr > p:cSldViewPr > p:cViewPr + $objWriter->startElement('p:cViewPr'); + + // p:viewPr > p:slideViewPr > p:cSldViewPr > p:cViewPr > p:scale + $objWriter->startElement('p:scale'); + + $objWriter->startElement('a:sx'); + $objWriter->writeAttribute('d', '100'); + $objWriter->writeAttribute('n', (int)($this->getPresentation()->getPresentationProperties()->getZoom() * 100)); + $objWriter->endElement(); + + $objWriter->startElement('a:sy'); + $objWriter->writeAttribute('d', '100'); + $objWriter->writeAttribute('n', (int)($this->getPresentation()->getPresentationProperties()->getZoom() * 100)); + $objWriter->endElement(); + + // > // p:viewPr > p:slideViewPr > p:cSldViewPr > p:cViewPr > p:scale + $objWriter->endElement(); + + $objWriter->startElement('p:origin'); + $objWriter->writeAttribute('x', '0'); + $objWriter->writeAttribute('y', '0'); + $objWriter->endElement(); + + // > // p:viewPr > p:slideViewPr > p:cSldViewPr > p:cViewPr + $objWriter->endElement(); + + // > // p:viewPr > p:slideViewPr > p:cSldViewPr + $objWriter->endElement(); + + // > // p:viewPr > p:slideViewPr + $objWriter->endElement(); + + // > // p:viewPr + $objWriter->endElement(); + + $this->getZip()->addFromString('ppt/viewProps.xml', $objWriter->getData()); + + return $this->getZip(); + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/Presentation.php b/src/PhpPresentation/Writer/PowerPoint2007/Presentation.php deleted file mode 100644 index 81605cdf0..000000000 --- a/src/PhpPresentation/Writer/PowerPoint2007/Presentation.php +++ /dev/null @@ -1,138 +0,0 @@ -getXMLWriter(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // p:presentation - $objWriter->startElement('p:presentation'); - $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); - $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); - $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main'); - - // p:sldMasterIdLst - $objWriter->startElement('p:sldMasterIdLst'); - - // Add slide masters - $relationId = 1; - $slideMasterId = 2147483648; - $parentWriter = $this->getParentWriter(); - if ($parentWriter instanceof PowerPoint2007) { - $masterSlides = $parentWriter->getLayoutPack()->getMasterSlides(); - $masterSlidesCount = count($masterSlides); - // @todo foreach ($masterSlides as $masterSlide) - for ($i = 0; $i < $masterSlidesCount; $i++) { - // p:sldMasterId - $objWriter->startElement('p:sldMasterId'); - $objWriter->writeAttribute('id', $slideMasterId); - $objWriter->writeAttribute('r:id', 'rId' . $relationId++); - $objWriter->endElement(); - - // Increase identifier - $slideMasterId += 12; - } - } - $objWriter->endElement(); - - // theme - $relationId++; - - // p:sldIdLst - $objWriter->startElement('p:sldIdLst'); - $this->writeSlides($objWriter, $pPhpPresentation, $relationId); - $objWriter->endElement(); - - // p:sldSz - $objWriter->startElement('p:sldSz'); - $objWriter->writeAttribute('cx', $pPhpPresentation->getLayout()->getCX()); - $objWriter->writeAttribute('cy', $pPhpPresentation->getLayout()->getCY()); - if ($pPhpPresentation->getLayout()->getDocumentLayout() != DocumentLayout::LAYOUT_CUSTOM) { - $objWriter->writeAttribute('type', $pPhpPresentation->getLayout()->getDocumentLayout()); - } - $objWriter->endElement(); - - // p:notesSz - $objWriter->startElement('p:notesSz'); - $objWriter->writeAttribute('cx', '6858000'); - $objWriter->writeAttribute('cy', '9144000'); - $objWriter->endElement(); - - $objWriter->endElement(); - - // Return - return $objWriter->getData(); - } - - /** - * Write slides - * - * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer - * @param PhpPresentation $pPhpPresentation - * @param int $startRelationId - * @throws \Exception - */ - private function writeSlides(XMLWriter $objWriter, PhpPresentation $pPhpPresentation, $startRelationId = 2) - { - // Write slides - $slideCount = $pPhpPresentation->getSlideCount(); - for ($i = 0; $i < $slideCount; ++$i) { - // p:sldId - $this->writeSlide($objWriter, ($i + 256), ($i + $startRelationId)); - } - } - - /** - * Write slide - * - * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer - * @param int $pSlideId Slide id - * @param int $pRelId Relationship ID - * @throws \Exception - */ - private function writeSlide(XMLWriter $objWriter, $pSlideId = 1, $pRelId = 1) - { - // p:sldId - $objWriter->startElement('p:sldId'); - $objWriter->writeAttribute('id', $pSlideId); - $objWriter->writeAttribute('r:id', 'rId' . $pRelId); - $objWriter->endElement(); - } -} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/Relationships.php b/src/PhpPresentation/Writer/PowerPoint2007/Relationships.php new file mode 100644 index 000000000..fc0af8095 --- /dev/null +++ b/src/PhpPresentation/Writer/PowerPoint2007/Relationships.php @@ -0,0 +1,128 @@ +getZip()->addFromString('_rels/.rels', $this->writeRelationships()); + $this->getZip()->addFromString('ppt/_rels/presentation.xml.rels', $this->writePresentationRelationships()); + + return $this->getZip(); + } + + /** + * Write relationships to XML format + * + * @return string XML Output + * @throws \Exception + */ + public function writeRelationships() + { + // Create XML writer + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); + + // XML header + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + + // Relationships + $objWriter->startElement('Relationships'); + $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); + // Relationship ppt/presentation.xml + $this->writeRelationship($objWriter, 1, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument', 'ppt/presentation.xml'); + // Relationship docProps/core.xml + $this->writeRelationship($objWriter, 2, 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties', 'docProps/core.xml'); + // Relationship docProps/app.xml + $this->writeRelationship($objWriter, 3, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties', 'docProps/app.xml'); + // Relationship docProps/custom.xml + $this->writeRelationship($objWriter, 4, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties', 'docProps/custom.xml'); + + $idxRelation = 5; + // Thumbnail + if ($this->getPresentation()->getPresentationProperties()->getThumbnailPath()) { + $pathThumbnail = file_get_contents($this->getPresentation()->getPresentationProperties()->getThumbnailPath()); + $gdImage = imagecreatefromstring($pathThumbnail); + if ($gdImage) { + imagedestroy($gdImage); + // Relationship docProps/thumbnail.jpeg + $this->writeRelationship($objWriter, $idxRelation, 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail', 'docProps/thumbnail.jpeg'); + } + } + // ++$idxRelation + + $objWriter->endElement(); + + // Return + return $objWriter->getData(); + } + + /** + * Write presentation relationships to XML format + * + * @return string XML Output + * @throws \Exception + */ + public function writePresentationRelationships() + { + // Create XML writer + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); + + // XML header + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + + // Relationships + $objWriter->startElement('Relationships'); + $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); + + // Relation id + $relationId = 1; + + foreach ($this->getPresentation()->getAllMasterSlides() as $oMasterSlide) { + // Relationship slideMasters/slideMasterX.xml + $this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster', 'slideMasters/slideMaster' . $oMasterSlide->getRelsIndex() . '.xml'); + } + + // Add slide theme (only one!) + // Relationship theme/theme1.xml + $this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme', 'theme/theme1.xml'); + + // Relationships with slides + $slideCount = $this->getPresentation()->getSlideCount(); + for ($i = 0; $i < $slideCount; ++$i) { + $this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slides/slide' . ($i + 1) . '.xml'); + } + + $this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps', 'presProps.xml'); + $this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps', 'viewProps.xml'); + $this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles', 'tableStyles.xml'); + + + // Comments Authors + foreach ($this->getPresentation()->getAllSlides() as $oSlide) { + foreach ($oSlide->getShapeCollection() as $oShape) { + if (!($oShape instanceof Comment)) { + continue; + } + $oAuthor = $oShape->getAuthor(); + if (!($oAuthor instanceof Author)) { + continue; + } + $this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/commentAuthors', 'commentAuthors.xml'); + break 2; + } + } + $objWriter->endElement(); + + // Return + return $objWriter->getData(); + } +} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/Rels.php b/src/PhpPresentation/Writer/PowerPoint2007/Rels.php deleted file mode 100644 index ba762faa2..000000000 --- a/src/PhpPresentation/Writer/PowerPoint2007/Rels.php +++ /dev/null @@ -1,583 +0,0 @@ -getXMLWriter(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // Relationships - $objWriter->startElement('Relationships'); - $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); - - // Relationship docProps/custom.xml - $this->writeRelationship($objWriter, 4, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties', 'docProps/custom.xml'); - - // Relationship docProps/app.xml - $this->writeRelationship($objWriter, 3, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties', 'docProps/app.xml'); - - // Relationship docProps/core.xml - $this->writeRelationship($objWriter, 2, 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties', 'docProps/core.xml'); - - // Relationship ppt/presentation.xml - $this->writeRelationship($objWriter, 1, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument', 'ppt/presentation.xml'); - - $objWriter->endElement(); - - // Return - return $objWriter->getData(); - } - - /** - * Write presentation relationships to XML format - * - * @param PhpPresentation $pPhpPresentation - * @return string XML Output - * @throws \Exception - */ - public function writePresentationRelationships(PhpPresentation $pPhpPresentation) - { - // Create XML writer - $objWriter = $this->getXMLWriter(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // Relationships - $objWriter->startElement('Relationships'); - $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); - - // Relation id - $relationId = 1; - - $parentWriter = $this->getParentWriter(); - if ($parentWriter instanceof PowerPoint2007) { - // Add slide masters - $masterSlides = $parentWriter->getLayoutPack()->getMasterSlides(); - foreach ($masterSlides as $masterSlide) { - // Relationship slideMasters/slideMasterX.xml - $this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster', 'slideMasters/slideMaster' . $masterSlide['masterid'] . '.xml'); - } - } - - // Add slide theme (only one!) - // Relationship theme/theme1.xml - $this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme', 'theme/theme1.xml'); - - // Relationships with slides - $slideCount = $pPhpPresentation->getSlideCount(); - for ($i = 0; $i < $slideCount; ++$i) { - $this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slides/slide' . ($i + 1) . '.xml'); - } - - $this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps', 'presProps.xml'); - $this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps', 'viewProps.xml'); - $this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles', 'tableStyles.xml'); - - $objWriter->endElement(); - - // Return - return $objWriter->getData(); - } - - /** - * Write slide master relationships to XML format - * - * @param int $masterId Master slide id - * @return string XML Output - * @throws \Exception - */ - public function writeSlideMasterRelationships($masterId = 1) - { - // Create XML writer - $objWriter = $this->getXMLWriter(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // Relationships - $objWriter->startElement('Relationships'); - $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); - - // Keep content id - $contentId = 0; - - $parentWriter = $this->getParentWriter(); - if ($parentWriter instanceof PowerPoint2007) { - // Lookup layouts - $layouts = array(); - $layoutPack = $parentWriter->getLayoutPack(); - foreach ($layoutPack->getLayouts() as $key => $layout) { - if ($layout['masterid'] == $masterId) { - $layouts[$key] = $layout; - } - } - - // Write slideLayout relationships - foreach ($layouts as $key => $layout) { - $this->writeRelationship($objWriter, ++$contentId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout', '../slideLayouts/slideLayout' . $key . '.xml'); - } - - // Relationship theme/theme1.xml - $this->writeRelationship($objWriter, ++$contentId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme', '../theme/theme' . $masterId . '.xml'); - - // Other relationships - $otherRelations = $layoutPack->getMasterSlideRelations(); - foreach ($otherRelations as $otherRelation) { - if ($otherRelation['masterid'] == $masterId) { - $this->writeRelationship($objWriter, ++$contentId, $otherRelation['type'], $otherRelation['target']); - } - } - } - $objWriter->endElement(); - - // Return - return $objWriter->getData(); - } - - /** - * Write slide layout relationships to XML format - * - * @param int $slideLayoutIndex - * @param int $masterId - * @return string XML Output - * @throws \Exception - */ - public function writeSlideLayoutRelationships($slideLayoutIndex, $masterId = 1) - { - // Create XML writer - $objWriter = $this->getXMLWriter(); - - $parentWriter = $this->getParentWriter(); - if ($parentWriter instanceof PowerPoint2007) { - // Layout pack - $layoutPack = $parentWriter->getLayoutPack(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // Relationships - $objWriter->startElement('Relationships'); - $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); - - // Write slideMaster relationship - $this->writeRelationship($objWriter, 1, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster', '../slideMasters/slideMaster' . $masterId . '.xml'); - - // Other relationships - $otherRelations = $layoutPack->getLayoutRelations(); - foreach ($otherRelations as $otherRelation) { - if ($otherRelation['layoutId'] == $slideLayoutIndex) { - $this->writeRelationship($objWriter, $otherRelation['id'], $otherRelation['type'], $otherRelation['target']); - } - } - - $objWriter->endElement(); - } - - // Return - return $objWriter->getData(); - } - - /** - * Write theme relationships to XML format - * - * @param int $masterId Master slide id - * @return string XML Output - * @throws \Exception - */ - public function writeThemeRelationships($masterId = 1) - { - // Create XML writer - $objWriter = $this->getXMLWriter(); - - $parentWriter = $this->getParentWriter(); - if ($parentWriter instanceof PowerPoint2007) { - // Layout pack - $layoutPack = $parentWriter->getLayoutPack(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // Relationships - $objWriter->startElement('Relationships'); - $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); - - // Other relationships - $otherRelations = $layoutPack->getThemeRelations(); - foreach ($otherRelations as $otherRelation) { - if ($otherRelation['masterid'] == $masterId) { - $this->writeRelationship($objWriter, $otherRelation['id'], $otherRelation['type'], $otherRelation['target']); - } - } - - $objWriter->endElement(); - } - // Return - return $objWriter->getData(); - } - - /** - * Write slide relationships to XML format - * - * @param \PhpOffice\PhpPresentation\Slide $pSlide - * @return string XML Output - * @throws \Exception - */ - public function writeSlideRelationships(SlideElement $pSlide) - { - // Create XML writer - $objWriter = $this->getXMLWriter(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // Relationships - $objWriter->startElement('Relationships'); - $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); - - // Starting relation id - $relId = 1; - $idxSlide = $pSlide->getParent()->getIndex($pSlide); - - // Write slideLayout relationship - $parentWriter = $this->getParentWriter(); - if ($parentWriter instanceof PowerPoint2007) { - $layoutPack = $parentWriter->getLayoutPack(); - $layoutId = $layoutPack->findlayoutId($pSlide->getSlideLayout(), $pSlide->getSlideMasterId()); - - $this->writeRelationship($objWriter, $relId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout', '../slideLayouts/slideLayout' . $layoutId . '.xml'); - } - - // Write drawing relationships? - if ($pSlide->getShapeCollection()->count() > 0) { - // Loop trough images and write relationships - $iterator = $pSlide->getShapeCollection()->getIterator(); - while ($iterator->valid()) { - if ($iterator->current() instanceof ShapeDrawing || $iterator->current() instanceof MemoryDrawing) { - // Write relationship for image drawing - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', '../media/' . str_replace(' ', '_', $iterator->current()->getIndexedFilename())); - - $iterator->current()->relationId = 'rId' . $relId; - - ++$relId; - } elseif ($iterator->current() instanceof ShapeChart) { - // Write relationship for chart drawing - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart', '../charts/' . $iterator->current()->getIndexedFilename()); - - $iterator->current()->relationId = 'rId' . $relId; - - ++$relId; - } elseif ($iterator->current() instanceof Group) { - $iterator2 = $iterator->current()->getShapeCollection()->getIterator(); - while ($iterator2->valid()) { - if ($iterator2->current() instanceof ShapeDrawing || $iterator2->current() instanceof MemoryDrawing) { - // Write relationship for image drawing - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', '../media/' . str_replace(' ', '_', $iterator2->current()->getIndexedFilename())); - - $iterator2->current()->relationId = 'rId' . $relId; - - ++$relId; - } elseif ($iterator2->current() instanceof ShapeChart) { - // Write relationship for chart drawing - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart', '../charts/' . $iterator2->current()->getIndexedFilename()); - - $iterator2->current()->relationId = 'rId' . $relId; - - ++$relId; - } - $iterator2->next(); - } - } - - $iterator->next(); - } - } - - // Write background relationships? - $oBackground = $pSlide->getBackground(); - if ($oBackground instanceof SlideElement\Background\Image) { - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', '../media/' . $oBackground->getIndexedFilename($idxSlide)); - $oBackground->relationId = 'rId' . $relId; - ++$relId; - } - - // Write hyperlink relationships? - if ($pSlide->getShapeCollection()->count() > 0) { - // Loop trough hyperlinks and write relationships - $iterator = $pSlide->getShapeCollection()->getIterator(); - while ($iterator->valid()) { - // Hyperlink on shape - if ($iterator->current()->hasHyperlink()) { - // Write relationship for hyperlink - $hyperlink = $iterator->current()->getHyperlink(); - $hyperlink->relationId = 'rId' . $relId; - - if (!$hyperlink->isInternal()) { - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External'); - } else { - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml'); - } - - ++$relId; - } - - // Hyperlink on rich text run - if ($iterator->current() instanceof RichText) { - foreach ($iterator->current()->getParagraphs() as $paragraph) { - foreach ($paragraph->getRichTextElements() as $element) { - if ($element instanceof Run || $element instanceof TextElement) { - if ($element->hasHyperlink()) { - // Write relationship for hyperlink - $hyperlink = $element->getHyperlink(); - $hyperlink->relationId = 'rId' . $relId; - - if (!$hyperlink->isInternal()) { - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External'); - } else { - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml'); - } - - ++$relId; - } - } - } - } - } - - // Hyperlink in table - if ($iterator->current() instanceof ShapeTable) { - // Rows - $countRows = count($iterator->current()->getRows()); - for ($row = 0; $row < $countRows; $row++) { - // Cells in rows - $countCells = count($iterator->current()->getRow($row)->getCells()); - for ($cell = 0; $cell < $countCells; $cell++) { - $currentCell = $iterator->current()->getRow($row)->getCell($cell); - // Paragraphs in cell - foreach ($currentCell->getParagraphs() as $paragraph) { - // RichText in paragraph - foreach ($paragraph->getRichTextElements() as $element) { - // Run or Text in RichText - if ($element instanceof Run || $element instanceof TextElement) { - if ($element->hasHyperlink()) { - // Write relationship for hyperlink - $hyperlink = $element->getHyperlink(); - $hyperlink->relationId = 'rId' . $relId; - - if (!$hyperlink->isInternal()) { - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External'); - } else { - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml'); - } - - ++$relId; - } - } - } - } - } - } - } - - if ($iterator->current() instanceof Group) { - $iterator2 = $pSlide->getShapeCollection()->getIterator(); - while ($iterator2->valid()) { - // Hyperlink on shape - if ($iterator2->current()->hasHyperlink()) { - // Write relationship for hyperlink - $hyperlink = $iterator2->current()->getHyperlink(); - $hyperlink->relationId = 'rId' . $relId; - - if (!$hyperlink->isInternal()) { - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External'); - } else { - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml'); - } - - ++$relId; - } - - // Hyperlink on rich text run - if ($iterator2->current() instanceof RichText) { - foreach ($iterator2->current()->getParagraphs() as $paragraph) { - foreach ($paragraph->getRichTextElements() as $element) { - if ($element instanceof Run || $element instanceof TextElement) { - if ($element->hasHyperlink()) { - // Write relationship for hyperlink - $hyperlink = $element->getHyperlink(); - $hyperlink->relationId = 'rId' . $relId; - - if (!$hyperlink->isInternal()) { - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External'); - } else { - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml'); - } - - ++$relId; - } - } - } - } - } - - // Hyperlink in table - if ($iterator2->current() instanceof ShapeTable) { - // Rows - $countRows = count($iterator2->current()->getRows()); - for ($row = 0; $row < $countRows; $row++) { - // Cells in rows - $countCells = count($iterator2->current()->getRow($row)->getCells()); - for ($cell = 0; $cell < $countCells; $cell++) { - $currentCell = $iterator2->current()->getRow($row)->getCell($cell); - // Paragraphs in cell - foreach ($currentCell->getParagraphs() as $paragraph) { - // RichText in paragraph - foreach ($paragraph->getRichTextElements() as $element) { - // Run or Text in RichText - if ($element instanceof Run || $element instanceof TextElement) { - if ($element->hasHyperlink()) { - // Write relationship for hyperlink - $hyperlink = $element->getHyperlink(); - $hyperlink->relationId = 'rId' . $relId; - - if (!$hyperlink->isInternal()) { - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External'); - } else { - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml'); - } - - ++$relId; - } - } - } - } - } - } - } - - $iterator2->next(); - } - - } - - $iterator->next(); - } - } - - if ($pSlide->getNote()->getShapeCollection()->count() > 0) { - $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide', '../notesSlides/notesSlide'.($idxSlide + 1).'.xml'); - ++$relId; - } - - $objWriter->endElement(); - - // Return - return $objWriter->getData(); - } - - /** - * Write chart relationships to XML format - * - * @param \PhpOffice\PhpPresentation\Shape\Chart $pChart - * @return string XML Output - * @throws \Exception - */ - public function writeChartRelationships(ShapeChart $pChart) - { - // Create XML writer - $objWriter = $this->getXMLWriter(); - - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // Relationships - $objWriter->startElement('Relationships'); - $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); - - // Write spreadsheet relationship? - if ($pChart->hasIncludedSpreadsheet()) { - $this->writeRelationship($objWriter, 1, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package', '../embeddings/' . $pChart->getIndexedFilename() . '.xlsx'); - } - - $objWriter->endElement(); - - // Return - return $objWriter->getData(); - } - - /** - * Write relationship - * - * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer - * @param int $pId Relationship ID. rId will be prepended! - * @param string $pType Relationship type - * @param string $pTarget Relationship target - * @param string $pTargetMode Relationship target mode - * @throws \Exception - */ - private function writeRelationship(XMLWriter $objWriter, $pId = 1, $pType = '', $pTarget = '', $pTargetMode = '') - { - if ($pType != '' && $pTarget != '') { - if (strpos($pId, 'rId') === false) { - $pId = 'rId' . $pId; - } - - // Write relationship - $objWriter->startElement('Relationship'); - $objWriter->writeAttribute('Id', $pId); - $objWriter->writeAttribute('Type', $pType); - $objWriter->writeAttribute('Target', $pTarget); - - if ($pTargetMode != '') { - $objWriter->writeAttribute('TargetMode', $pTargetMode); - } - - $objWriter->endElement(); - } else { - throw new \Exception("Invalid parameters passed."); - } - } -} diff --git a/src/PhpPresentation/Writer/PowerPoint2007/Theme.php b/src/PhpPresentation/Writer/PowerPoint2007/Theme.php deleted file mode 100644 index f726a3a7f..000000000 --- a/src/PhpPresentation/Writer/PowerPoint2007/Theme.php +++ /dev/null @@ -1,48 +0,0 @@ -getParentWriter(); - if ($parentWriter instanceof PowerPoint2007) { - $layoutPack = $parentWriter->getLayoutPack(); - foreach ($layoutPack->getThemes() as $theme) { - if ($theme['masterid'] == $masterId) { - return $theme['body']; - } - } - throw new \Exception('No theme has been found!'); - } - } -} diff --git a/src/PhpPresentation/Writer/Serialized.php b/src/PhpPresentation/Writer/Serialized.php index 047e53047..9f9f6b5a9 100644 --- a/src/PhpPresentation/Writer/Serialized.php +++ b/src/PhpPresentation/Writer/Serialized.php @@ -17,6 +17,7 @@ namespace PhpOffice\PhpPresentation\Writer; +use PhpOffice\Common\Adapter\Zip\ZipArchiveAdapter; use PhpOffice\Common\XMLWriter; use PhpOffice\PhpPresentation\PhpPresentation; use PhpOffice\PhpPresentation\Shape\AbstractDrawing; @@ -24,15 +25,8 @@ /** * \PhpOffice\PhpPresentation\Writer\Serialized */ -class Serialized implements WriterInterface +class Serialized extends AbstractWriter implements WriterInterface { - /** - * Private PhpPresentation - * - * @var \PhpOffice\PhpPresentation\PhpPresentation - */ - private $presentation; - /** * Create a new \PhpOffice\PhpPresentation\Writer\Serialized * @@ -40,8 +34,11 @@ class Serialized implements WriterInterface */ public function __construct(PhpPresentation $pPhpPresentation = null) { - // Assign PhpPresentation + // Set PhpPresentation $this->setPhpPresentation($pPhpPresentation); + + // Set ZIP Adapter + $this->setZipAdapter(new ZipArchiveAdapter()); } /** @@ -55,67 +52,30 @@ public function save($pFilename) if (empty($pFilename)) { throw new \Exception("Filename is empty."); } - if (!is_null($this->presentation)) { - // Create new ZIP file and open it for writing - $objZip = new \ZipArchive(); - - // Try opening the ZIP file - if ($objZip->open($pFilename, \ZipArchive::CREATE) !== true) { - if ($objZip->open($pFilename, \ZipArchive::OVERWRITE) !== true) { - throw new \Exception("Could not open " . $pFilename . " for writing."); - } - } + $oPresentation = $this->getPhpPresentation(); - // Add media - $slideCount = $this->presentation->getSlideCount(); - for ($i = 0; $i < $slideCount; ++$i) { - for ($j = 0; $j < $this->presentation->getSlide($i)->getShapeCollection()->count(); ++$j) { - if ($this->presentation->getSlide($i)->getShapeCollection()->offsetGet($j) instanceof AbstractDrawing) { - $imgTemp = $this->presentation->getSlide($i)->getShapeCollection()->offsetGet($j); - $objZip->addFromString('media/' . $imgTemp->getFilename(), file_get_contents($imgTemp->getPath())); - } - } - } + // Create new ZIP file and open it for writing + $objZip = $this->getZipAdapter(); - // Add PhpPresentation.xml to the document, which represents a PHP serialized PhpPresentation object - $objZip->addFromString('PhpPresentation.xml', $this->writeSerialized($this->presentation, $pFilename)); + // Try opening the ZIP file + $objZip->open($pFilename); - // Close file - if ($objZip->close() === false) { - throw new \Exception("Could not close zip file $pFilename."); + // Add media + $slideCount = $oPresentation->getSlideCount(); + for ($i = 0; $i < $slideCount; ++$i) { + for ($j = 0; $j < $oPresentation->getSlide($i)->getShapeCollection()->count(); ++$j) { + if ($oPresentation->getSlide($i)->getShapeCollection()->offsetGet($j) instanceof AbstractDrawing) { + $imgTemp = $oPresentation->getSlide($i)->getShapeCollection()->offsetGet($j); + $objZip->addFromString('media/' . $imgTemp->getFilename(), file_get_contents($imgTemp->getPath())); + } } - } else { - throw new \Exception("PhpPresentation object unassigned."); } - } - /** - * Get PhpPresentation object - * - * @return PhpPresentation - * @throws \Exception - */ - public function getPhpPresentation() - { - if (!is_null($this->presentation)) { - return $this->presentation; - } else { - throw new \Exception("No PhpPresentation assigned."); - } - } - - /** - * Get PhpPresentation object - * - * @param PhpPresentation $pPhpPresentation PhpPresentation object - * @throws \Exception - * @return \PhpOffice\PhpPresentation\Writer\Serialized - */ - public function setPhpPresentation(PhpPresentation $pPhpPresentation = null) - { - $this->presentation = $pPhpPresentation; + // Add PhpPresentation.xml to the document, which represents a PHP serialized PhpPresentation object + $objZip->addFromString('PhpPresentation.xml', $this->writeSerialized($oPresentation, $pFilename)); - return $this; + // Close file + $objZip->close(); } /** diff --git a/tests/PhpPresentation/Tests/AbstractShapeTest.php b/tests/PhpPresentation/Tests/AbstractShapeTest.php index 345366d0d..09212eca7 100644 --- a/tests/PhpPresentation/Tests/AbstractShapeTest.php +++ b/tests/PhpPresentation/Tests/AbstractShapeTest.php @@ -17,6 +17,7 @@ namespace PhpOffice\PhpPresentation\Tests; +use PhpOffice\PhpPresentation\Shape\Placeholder; use PhpOffice\PhpPresentation\Slide; use PhpOffice\PhpPresentation\Shape\Hyperlink; use PhpOffice\PhpPresentation\Shape\RichText; @@ -151,4 +152,26 @@ public function testWidthAndHeight() $this->assertEquals($value, $object->getWidth()); $this->assertEquals($value, $object->getHeight()); } + + public function testPlaceholder() + { + $object = new RichText(); + $this->assertFalse($object->isPlaceholder(), 'Standard Shape should not be a placeholder object'); + $this->assertInstanceOf( + 'PhpOffice\\PhpPresentation\\AbstractShape', + $object->setPlaceHolder(new Placeholder(Placeholder::PH_TYPE_TITLE)) + ); + $this->assertTrue($object->isPlaceholder()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Placeholder', $object->getPlaceholder()); + $this->assertEquals('title', $object->getPlaceholder()->getType()); + + $object = new RichText(); + $this->assertFalse($object->isPlaceholder(), 'Standard Shape should not be a placeholder object'); + $placeholder = new Placeholder(Placeholder::PH_TYPE_TITLE); + $placeholder->setType(Placeholder::PH_TYPE_SUBTITLE); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\AbstractShape', $object->setPlaceHolder($placeholder)); + $this->assertTrue($object->isPlaceholder()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Placeholder', $object->getPlaceholder()); + $this->assertEquals('subTitle', $object->getPlaceholder()->getType()); + } } diff --git a/tests/PhpPresentation/Tests/PhpPowerpointTest.php b/tests/PhpPresentation/Tests/PhpPresentationTest.php similarity index 73% rename from tests/PhpPresentation/Tests/PhpPowerpointTest.php rename to tests/PhpPresentation/Tests/PhpPresentationTest.php index 13b4805ac..549d0b611 100644 --- a/tests/PhpPresentation/Tests/PhpPowerpointTest.php +++ b/tests/PhpPresentation/Tests/PhpPresentationTest.php @@ -20,6 +20,7 @@ use PhpOffice\PhpPresentation\DocumentLayout; use PhpOffice\PhpPresentation\DocumentProperties; use PhpOffice\PhpPresentation\PhpPresentation; +use PhpOffice\PhpPresentation\PresentationProperties; /** * Test class for PhpPresentation @@ -36,7 +37,7 @@ public function testConstruct() $object = new PhpPresentation(); $slide = $object->getSlide(); - $this->assertEquals(new DocumentProperties(), $object->getProperties()); + $this->assertEquals(new DocumentProperties(), $object->getDocumentProperties()); $this->assertEquals(new DocumentLayout(), $object->getLayout()); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide', $object->getSlide()); $this->assertEquals(1, count($object->getAllSlides())); @@ -46,6 +47,27 @@ public function testConstruct() $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\Iterator', $object->getSlideIterator()); } + public function testProperties() + { + $object = new PhpPresentation(); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\DocumentProperties', $object->getProperties()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\DocumentProperties', $object->getDocumentProperties()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $object->setProperties(new DocumentProperties())); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\DocumentProperties', $object->getProperties()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\DocumentProperties', $object->getDocumentProperties()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $object->setDocumentProperties(new DocumentProperties())); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\DocumentProperties', $object->getProperties()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\DocumentProperties', $object->getDocumentProperties()); + } + + public function testPresentationProperties() + { + $object = new PhpPresentation(); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->getPresentationProperties()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $object->setPresentationProperties(new PresentationProperties())); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->getPresentationProperties()); + } + /** * Test add external slide */ @@ -92,20 +114,6 @@ public function testGetSlideException() $object->getSlide(1); } - public function testMarkAsFinal() - { - $object = new PhpPresentation(); - $this->assertFalse($object->isMarkedAsFinal()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $object->markAsFinal('AAAA')); - $this->assertFalse($object->isMarkedAsFinal()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $object->markAsFinal(true)); - $this->assertTrue($object->isMarkedAsFinal()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $object->markAsFinal(false)); - $this->assertFalse($object->isMarkedAsFinal()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $object->markAsFinal()); - $this->assertTrue($object->isMarkedAsFinal()); - } - /** * Test set active slide index exception * @@ -117,16 +125,4 @@ public function testSetActiveSlideIndexException() $object = new PhpPresentation(); $object->setActiveSlideIndex(1); } - - public function testZoom() - { - $object = new PhpPresentation(); - $this->assertEquals(1, $object->getZoom()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $object->setZoom('AAAA')); - $this->assertEquals(1, $object->getZoom()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $object->setZoom(2.3)); - $this->assertEquals(2.3, $object->getZoom()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $object->setZoom()); - $this->assertEquals(1, $object->getZoom()); - } } diff --git a/tests/PhpPresentation/Tests/PresentationPropertiesTest.php b/tests/PhpPresentation/Tests/PresentationPropertiesTest.php new file mode 100644 index 000000000..5350b6595 --- /dev/null +++ b/tests/PhpPresentation/Tests/PresentationPropertiesTest.php @@ -0,0 +1,110 @@ +assertFalse($object->isCommentVisible()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setCommentVisible('AAAA')); + $this->assertFalse($object->isCommentVisible()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setCommentVisible(true)); + $this->assertTrue($object->isCommentVisible()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setCommentVisible(false)); + $this->assertFalse($object->isCommentVisible()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setCommentVisible()); + $this->assertFalse($object->isCommentVisible()); + } + + public function testLoopUntilEsc() + { + $object = new PresentationProperties(); + $this->assertFalse($object->isLoopContinuouslyUntilEsc()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setLoopContinuouslyUntilEsc('AAAA')); + $this->assertFalse($object->isLoopContinuouslyUntilEsc()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setLoopContinuouslyUntilEsc(true)); + $this->assertTrue($object->isLoopContinuouslyUntilEsc()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setLoopContinuouslyUntilEsc(false)); + $this->assertFalse($object->isLoopContinuouslyUntilEsc()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setLoopContinuouslyUntilEsc()); + $this->assertFalse($object->isLoopContinuouslyUntilEsc()); + } + + public function testLastView() + { + $object = new PresentationProperties(); + $this->assertEquals(PresentationProperties::VIEW_SLIDE, $object->getLastView()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setLastView('AAAA')); + $this->assertEquals(PresentationProperties::VIEW_SLIDE, $object->getLastView()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setLastView(PresentationProperties::VIEW_OUTLINE)); + $this->assertEquals(PresentationProperties::VIEW_OUTLINE, $object->getLastView()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setLastView()); + $this->assertEquals(PresentationProperties::VIEW_SLIDE, $object->getLastView()); + } + + public function testMarkAsFinal() + { + $object = new PresentationProperties(); + $this->assertFalse($object->isMarkedAsFinal()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->markAsFinal('AAAA')); + $this->assertFalse($object->isMarkedAsFinal()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->markAsFinal(true)); + $this->assertTrue($object->isMarkedAsFinal()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->markAsFinal(false)); + $this->assertFalse($object->isMarkedAsFinal()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->markAsFinal()); + $this->assertTrue($object->isMarkedAsFinal()); + } + + public function testThumbnail() + { + $imagePath = PHPPRESENTATION_TESTS_BASE_DIR.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.'PhpPresentationLogo.png'; + + $object = new PresentationProperties(); + $this->assertNull($object->getThumbnailPath()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setThumbnailPath('AAAA')); + $this->assertNull($object->getThumbnailPath()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setThumbnailPath()); + $this->assertNull($object->getThumbnailPath()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setThumbnailPath($imagePath)); + $this->assertEquals($imagePath, $object->getThumbnailPath()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setThumbnailPath()); + $this->assertEquals($imagePath, $object->getThumbnailPath()); + } + + public function testZoom() + { + $object = new PresentationProperties(); + $this->assertEquals(1, $object->getZoom()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setZoom('AAAA')); + $this->assertEquals(1, $object->getZoom()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setZoom(2.3)); + $this->assertEquals(2.3, $object->getZoom()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PresentationProperties', $object->setZoom()); + $this->assertEquals(1, $object->getZoom()); + } +} diff --git a/tests/PhpPresentation/Tests/Reader/ODPresentationTest.php b/tests/PhpPresentation/Tests/Reader/ODPresentationTest.php index 6e1287e45..5e6bb66f4 100644 --- a/tests/PhpPresentation/Tests/Reader/ODPresentationTest.php +++ b/tests/PhpPresentation/Tests/Reader/ODPresentationTest.php @@ -86,12 +86,12 @@ public function testLoadFile01() $oPhpPresentation = $object->load($file); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $oPhpPresentation); // Document Properties - $this->assertEquals('PHPOffice', $oPhpPresentation->getProperties()->getCreator()); - $this->assertEquals('PHPPresentation Team', $oPhpPresentation->getProperties()->getLastModifiedBy()); - $this->assertEquals('Sample 02 Title', $oPhpPresentation->getProperties()->getTitle()); - $this->assertEquals('Sample 02 Subject', $oPhpPresentation->getProperties()->getSubject()); - $this->assertEquals('Sample 02 Description', $oPhpPresentation->getProperties()->getDescription()); - $this->assertEquals('office 2007 openxml libreoffice odt php', $oPhpPresentation->getProperties()->getKeywords()); + $this->assertEquals('PHPOffice', $oPhpPresentation->getDocumentProperties()->getCreator()); + $this->assertEquals('PHPPresentation Team', $oPhpPresentation->getDocumentProperties()->getLastModifiedBy()); + $this->assertEquals('Sample 02 Title', $oPhpPresentation->getDocumentProperties()->getTitle()); + $this->assertEquals('Sample 02 Subject', $oPhpPresentation->getDocumentProperties()->getSubject()); + $this->assertEquals('Sample 02 Description', $oPhpPresentation->getDocumentProperties()->getDescription()); + $this->assertEquals('office 2007 openxml libreoffice odt php', $oPhpPresentation->getDocumentProperties()->getKeywords()); // $this->assertCount(4, $oPhpPresentation->getAllSlides()); @@ -101,7 +101,7 @@ public function testLoadFile01() $this->assertCount(2, $arrayShape); // Slide 1 : Shape 1 $oShape = $arrayShape[0]; - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\MemoryDrawing', $oShape); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing\\Gd', $oShape); $this->assertEquals('PHPPresentation logo', $oShape->getName()); $this->assertEquals('PHPPresentation logo', $oShape->getDescription()); $this->assertEquals(36, $oShape->getHeight()); @@ -147,7 +147,7 @@ public function testLoadFile01() $this->assertCount(3, $arrayShape); // Slide 2 : Shape 1 $oShape = $arrayShape[0]; - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\MemoryDrawing', $oShape); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing\\Gd', $oShape); $this->assertEquals('PHPPresentation logo', $oShape->getName()); $this->assertEquals('PHPPresentation logo', $oShape->getDescription()); $this->assertEquals(36, $oShape->getHeight()); @@ -244,7 +244,7 @@ public function testLoadFile01() $this->assertCount(3, $arrayShape); // Slide 3 : Shape 1 $oShape = $arrayShape[0]; - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\MemoryDrawing', $oShape); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing\\Gd', $oShape); $this->assertEquals('PHPPresentation logo', $oShape->getName()); $this->assertEquals('PHPPresentation logo', $oShape->getDescription()); $this->assertEquals(36, $oShape->getHeight()); @@ -401,7 +401,7 @@ public function testLoadFile01() $this->assertCount(3, $arrayShape); // Slide 4 : Shape 1 $oShape = $arrayShape[0]; - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\MemoryDrawing', $oShape); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing\\Gd', $oShape); $this->assertEquals('PHPPresentation logo', $oShape->getName()); $this->assertEquals('PHPPresentation logo', $oShape->getDescription()); $this->assertEquals(36, $oShape->getHeight()); diff --git a/tests/PhpPresentation/Tests/Reader/PowerPoint2007Test.php b/tests/PhpPresentation/Tests/Reader/PowerPoint2007Test.php index 084d5ed96..2a2c5f4f9 100644 --- a/tests/PhpPresentation/Tests/Reader/PowerPoint2007Test.php +++ b/tests/PhpPresentation/Tests/Reader/PowerPoint2007Test.php @@ -17,6 +17,7 @@ namespace PhpOffice\PhpPresentation\Tests\Reader; +use PhpOffice\PhpPresentation\DocumentLayout; use PhpOffice\PhpPresentation\Reader\PowerPoint2007; use PhpOffice\PhpPresentation\Style\Alignment; use PhpOffice\PhpPresentation\Style\Bullet; @@ -83,14 +84,19 @@ public function testLoadFile01() $oPhpPresentation = $object->load($file); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $oPhpPresentation); // Document Properties - $this->assertEquals('PHPOffice', $oPhpPresentation->getProperties()->getCreator()); - $this->assertEquals('PHPPresentation Team', $oPhpPresentation->getProperties()->getLastModifiedBy()); - $this->assertEquals('Sample 02 Title', $oPhpPresentation->getProperties()->getTitle()); - $this->assertEquals('Sample 02 Subject', $oPhpPresentation->getProperties()->getSubject()); - $this->assertEquals('Sample 02 Description', $oPhpPresentation->getProperties()->getDescription()); - $this->assertEquals('office 2007 openxml libreoffice odt php', $oPhpPresentation->getProperties()->getKeywords()); - $this->assertEquals('Sample Category', $oPhpPresentation->getProperties()->getCategory()); - // + $this->assertEquals('PHPOffice', $oPhpPresentation->getDocumentProperties()->getCreator()); + $this->assertEquals('PHPPresentation Team', $oPhpPresentation->getDocumentProperties()->getLastModifiedBy()); + $this->assertEquals('Sample 02 Title', $oPhpPresentation->getDocumentProperties()->getTitle()); + $this->assertEquals('Sample 02 Subject', $oPhpPresentation->getDocumentProperties()->getSubject()); + $this->assertEquals('Sample 02 Description', $oPhpPresentation->getDocumentProperties()->getDescription()); + $this->assertEquals('office 2007 openxml libreoffice odt php', $oPhpPresentation->getDocumentProperties()->getKeywords()); + $this->assertEquals('Sample Category', $oPhpPresentation->getDocumentProperties()->getCategory()); + // Document Layout + $this->assertEquals(DocumentLayout::LAYOUT_SCREEN_4X3, $oPhpPresentation->getLayout()->getDocumentLayout()); + $this->assertEquals(254, $oPhpPresentation->getLayout()->getCX(DocumentLayout::UNIT_MILLIMETER)); + $this->assertEquals(190.5, $oPhpPresentation->getLayout()->getCY(DocumentLayout::UNIT_MILLIMETER)); + + // Slides $this->assertCount(4, $oPhpPresentation->getAllSlides()); // Slide 1 @@ -99,7 +105,7 @@ public function testLoadFile01() $this->assertCount(2, $arrayShape); // Slide 1 : Shape 1 $oShape = $arrayShape[0]; - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\MemoryDrawing', $oShape); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing\\Gd', $oShape); $this->assertEquals('PHPPresentation logo', $oShape->getName()); $this->assertEquals('PHPPresentation logo', $oShape->getDescription()); $this->assertEquals(36, $oShape->getHeight()); @@ -145,7 +151,7 @@ public function testLoadFile01() $this->assertCount(3, $arrayShape); // Slide 2 : Shape 1 $oShape = $arrayShape[0]; - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\MemoryDrawing', $oShape); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing\\Gd', $oShape); $this->assertEquals('PHPPresentation logo', $oShape->getName()); $this->assertEquals('PHPPresentation logo', $oShape->getDescription()); $this->assertEquals(36, $oShape->getHeight()); @@ -242,7 +248,7 @@ public function testLoadFile01() $this->assertCount(3, $arrayShape); // Slide 3 : Shape 1 $oShape = $arrayShape[0]; - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\MemoryDrawing', $oShape); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing\\Gd', $oShape); $this->assertEquals('PHPPresentation logo', $oShape->getName()); $this->assertEquals('PHPPresentation logo', $oShape->getDescription()); $this->assertEquals(36, $oShape->getHeight()); @@ -399,7 +405,7 @@ public function testLoadFile01() $this->assertCount(3, $arrayShape); // Slide 4 : Shape 1 $oShape = $arrayShape[0]; - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\MemoryDrawing', $oShape); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing\\Gd', $oShape); $this->assertEquals('PHPPresentation logo', $oShape->getName()); $this->assertEquals('PHPPresentation logo', $oShape->getDescription()); $this->assertEquals(36, $oShape->getHeight()); @@ -469,14 +475,14 @@ public function testMarkAsFinal() $object = new PowerPoint2007(); $oPhpPresentation = $object->load($file); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $oPhpPresentation); - $this->assertFalse($oPhpPresentation->isMarkedAsFinal()); + $this->assertFalse($oPhpPresentation->getPresentationProperties()->isMarkedAsFinal()); $file = PHPPRESENTATION_TESTS_BASE_DIR . '/resources/files/PPTX_MarkAsFinal.pptx'; $object = new PowerPoint2007(); $oPhpPresentation = $object->load($file); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $oPhpPresentation); - $this->assertTrue($oPhpPresentation->isMarkedAsFinal()); + $this->assertTrue($oPhpPresentation->getPresentationProperties()->isMarkedAsFinal()); } public function testZoom() @@ -485,13 +491,27 @@ public function testZoom() $object = new PowerPoint2007(); $oPhpPresentation = $object->load($file); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $oPhpPresentation); - $this->assertEquals(1, $oPhpPresentation->getZoom()); + $this->assertEquals(1, $oPhpPresentation->getPresentationProperties()->getZoom()); $file = PHPPRESENTATION_TESTS_BASE_DIR . '/resources/files/PPTX_Zoom.pptx'; $object = new PowerPoint2007(); $oPhpPresentation = $object->load($file); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $oPhpPresentation); - $this->assertEquals(2.68, $oPhpPresentation->getZoom()); + $this->assertEquals(2.68, $oPhpPresentation->getPresentationProperties()->getZoom()); + } + + public function testSlideLayout() + { + $file = PHPPRESENTATION_TESTS_BASE_DIR . '/resources/files/Issue_00150.pptx'; + $object = new PowerPoint2007(); + $oPhpPresentation = $object->load($file); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $oPhpPresentation); + + $masterSlides = $oPhpPresentation->getAllMasterSlides(); + $this->assertCount(3, $masterSlides); + $this->assertCount(11, $masterSlides[0]->getAllSlideLayouts()); + $this->assertCount(11, $masterSlides[1]->getAllSlideLayouts()); + $this->assertCount(11, $masterSlides[2]->getAllSlideLayouts()); } } diff --git a/tests/PhpPresentation/Tests/Shape/Chart/AxisTest.php b/tests/PhpPresentation/Tests/Shape/Chart/AxisTest.php index 416450149..bb23a2f14 100644 --- a/tests/PhpPresentation/Tests/Shape/Chart/AxisTest.php +++ b/tests/PhpPresentation/Tests/Shape/Chart/AxisTest.php @@ -17,120 +17,72 @@ namespace PhpOffice\PhpPresentation\Tests\Shape\Chart; -use PhpOffice\PhpPresentation\Shape\Chart\Legend; -use PhpOffice\PhpPresentation\Style\Alignment; -use PhpOffice\PhpPresentation\Style\Border; -use PhpOffice\PhpPresentation\Style\Fill; +use PhpOffice\PhpPresentation\Shape\Chart\Axis; use PhpOffice\PhpPresentation\Style\Font; /** - * Test class for Legend element + * Test class for Axis element * - * @coversDefaultClass PhpOffice\PhpPresentation\Shape\Chart\Legend + * @coversDefaultClass PhpOffice\PhpPresentation\Shape\Chart\Axis */ class AxisTest extends \PHPUnit_Framework_TestCase { public function testConstruct() { - $object = new Legend(); + $object = new Axis(); + $this->assertEquals('Axis Title', $object->getTitle()); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Font', $object->getFont()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Border', $object->getBorder()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Fill', $object->getFill()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Alignment', $object->getAlignment()); - } - - public function testAlignment() - { - $object = new Legend(); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setAlignment(new Alignment())); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Alignment', $object->getAlignment()); + $this->assertNull($object->getMinorGridlines()); + $this->assertNull($object->getMajorGridlines()); } public function testFont() { - $object = new Legend(); + $object = new Axis(); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setFont()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Axis', $object->setFont()); $this->assertNull($object->getFont()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setFont(new Font())); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Axis', $object->setFont(new Font())); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Font', $object->getFont()); } - public function testHashIndex() + public function testFormatCode() { - $object = new Legend(); - $value = rand(1, 100); - - $this->assertEmpty($object->getHashIndex()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setHashIndex($value)); - $this->assertEquals($value, $object->getHashIndex()); + $object = new Axis(); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Axis', $object->setFormatCode()); + $this->assertEquals('', $object->getFormatCode()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Axis', $object->setFormatCode('AAAA')); + $this->assertEquals('AAAA', $object->getFormatCode()); } - public function testHeight() + public function testGridLines() { - $object = new Legend(); - $value = rand(0, 100); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setHeight()); - $this->assertEquals(0, $object->getHeight()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setHeight($value)); - $this->assertEquals($value, $object->getHeight()); - } + $object = new Axis(); - public function testOffsetX() - { - $object = new Legend(); - $value = rand(0, 100); + $oMock = $this->getMock('PhpOffice\PhpPresentation\Shape\Chart\Gridlines'); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setOffsetX()); - $this->assertEquals(0, $object->getOffsetX()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setOffsetX($value)); - $this->assertEquals($value, $object->getOffsetX()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Axis', $object->setMajorGridlines($oMock)); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Gridlines', $object->getMajorGridlines()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Axis', $object->setMinorGridlines($oMock)); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Gridlines', $object->getMinorGridlines()); } - public function testOffsetY() - { - $object = new Legend(); - $value = rand(0, 100); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setOffsetY()); - $this->assertEquals(0, $object->getOffsetY()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setOffsetY($value)); - $this->assertEquals($value, $object->getOffsetY()); - } - - public function testPosition() - { - $object = new Legend(); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setPosition()); - $this->assertEquals(Legend::POSITION_RIGHT, $object->getPosition()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setPosition(Legend::POSITION_BOTTOM)); - $this->assertEquals(Legend::POSITION_BOTTOM, $object->getPosition()); - } - - public function testVisible() + public function testHashIndex() { - $object = new Legend(); + $object = new Axis(); + $value = rand(1, 100); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setVisible()); - $this->assertTrue($object->isVisible()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setVisible(true)); - $this->assertTrue($object->isVisible()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setVisible(false)); - $this->assertFalse($object->isVisible()); + $this->assertEmpty($object->getHashIndex()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Axis', $object->setHashIndex($value)); + $this->assertEquals($value, $object->getHashIndex()); } - public function testWidth() + public function testTitle() { - $object = new Legend(); - $value = rand(0, 100); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setWidth()); - $this->assertEquals(0, $object->getWidth()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setWidth($value)); - $this->assertEquals($value, $object->getWidth()); + $object = new Axis(); + $this->assertEquals('Axis Title', $object->getTitle()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Axis', $object->setTitle('AAAA')); + $this->assertEquals('AAAA', $object->getTitle()); } } diff --git a/tests/PhpPresentation/Tests/Shape/Chart/GridlinesTest.php b/tests/PhpPresentation/Tests/Shape/Chart/GridlinesTest.php new file mode 100644 index 000000000..658062945 --- /dev/null +++ b/tests/PhpPresentation/Tests/Shape/Chart/GridlinesTest.php @@ -0,0 +1,26 @@ +assertInstanceOf('PhpOffice\PhpPresentation\Style\Outline', $object->getOutline()); + } + + public function testGetSetOutline() + { + $object = new Gridlines(); + + $oStub = $this->getMock('PhpOffice\PhpPresentation\Style\Outline'); + + $this->assertInstanceOf('PhpOffice\PhpPresentation\Style\Outline', $object->getOutline()); + $this->assertInstanceOf('PhpOffice\PhpPresentation\Shape\Chart\Gridlines', $object->setOutline($oStub)); + $this->assertInstanceOf('PhpOffice\PhpPresentation\Style\Outline', $object->getOutline()); + } +} diff --git a/tests/PhpPresentation/Tests/Shape/Chart/LegendTest.php b/tests/PhpPresentation/Tests/Shape/Chart/LegendTest.php index 9ee1858b0..ca94d7107 100644 --- a/tests/PhpPresentation/Tests/Shape/Chart/LegendTest.php +++ b/tests/PhpPresentation/Tests/Shape/Chart/LegendTest.php @@ -17,46 +17,120 @@ namespace PhpOffice\PhpPresentation\Tests\Shape\Chart; -use PhpOffice\PhpPresentation\Shape\Chart\Axis; +use PhpOffice\PhpPresentation\Shape\Chart\Legend; +use PhpOffice\PhpPresentation\Style\Alignment; +use PhpOffice\PhpPresentation\Style\Border; +use PhpOffice\PhpPresentation\Style\Fill; +use PhpOffice\PhpPresentation\Style\Font; /** - * Test class for Axis element + * Test class for Legend element * - * @coversDefaultClass PhpOffice\PhpPresentation\Shape\Chart\Axis + * @coversDefaultClass PhpOffice\PhpPresentation\Shape\Chart\Legend */ class LegendTest extends \PHPUnit_Framework_TestCase { public function testConstruct() { - $object = new Axis(); + $object = new Legend(); - $this->assertEquals('Axis Title', $object->getTitle()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Font', $object->getFont()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Border', $object->getBorder()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Fill', $object->getFill()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Alignment', $object->getAlignment()); } - public function testFormatCode() + public function testAlignment() { - $object = new Axis(); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Axis', $object->setFormatCode()); - $this->assertEquals('', $object->getFormatCode()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Axis', $object->setFormatCode('AAAA')); - $this->assertEquals('AAAA', $object->getFormatCode()); + $object = new Legend(); + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setAlignment(new Alignment())); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Alignment', $object->getAlignment()); + } + + public function testFont() + { + $object = new Legend(); + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setFont()); + $this->assertNull($object->getFont()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setFont(new Font())); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Font', $object->getFont()); } public function testHashIndex() { - $object = new Axis(); + $object = new Legend(); $value = rand(1, 100); $this->assertEmpty($object->getHashIndex()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Axis', $object->setHashIndex($value)); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setHashIndex($value)); $this->assertEquals($value, $object->getHashIndex()); } - public function testTitle() + public function testHeight() + { + $object = new Legend(); + $value = rand(0, 100); + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setHeight()); + $this->assertEquals(0, $object->getHeight()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setHeight($value)); + $this->assertEquals($value, $object->getHeight()); + } + + public function testOffsetX() + { + $object = new Legend(); + $value = rand(0, 100); + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setOffsetX()); + $this->assertEquals(0, $object->getOffsetX()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setOffsetX($value)); + $this->assertEquals($value, $object->getOffsetX()); + } + + public function testOffsetY() { - $object = new Axis(); - $this->assertEquals('Axis Title', $object->getTitle()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Axis', $object->setTitle('AAAA')); - $this->assertEquals('AAAA', $object->getTitle()); + $object = new Legend(); + $value = rand(0, 100); + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setOffsetY()); + $this->assertEquals(0, $object->getOffsetY()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setOffsetY($value)); + $this->assertEquals($value, $object->getOffsetY()); + } + + public function testPosition() + { + $object = new Legend(); + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setPosition()); + $this->assertEquals(Legend::POSITION_RIGHT, $object->getPosition()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setPosition(Legend::POSITION_BOTTOM)); + $this->assertEquals(Legend::POSITION_BOTTOM, $object->getPosition()); + } + + public function testVisible() + { + $object = new Legend(); + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setVisible()); + $this->assertTrue($object->isVisible()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setVisible(true)); + $this->assertTrue($object->isVisible()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setVisible(false)); + $this->assertFalse($object->isVisible()); + } + + public function testWidth() + { + $object = new Legend(); + $value = rand(0, 100); + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setWidth()); + $this->assertEquals(0, $object->getWidth()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Legend', $object->setWidth($value)); + $this->assertEquals($value, $object->getWidth()); } } diff --git a/tests/PhpPresentation/Tests/Shape/Chart/MarkerTest.php b/tests/PhpPresentation/Tests/Shape/Chart/MarkerTest.php new file mode 100644 index 000000000..d351d9460 --- /dev/null +++ b/tests/PhpPresentation/Tests/Shape/Chart/MarkerTest.php @@ -0,0 +1,55 @@ +assertEquals(Marker::SYMBOL_NONE, $object->getSymbol()); + $this->assertEquals(5, $object->getSize()); + } + + public function testSymbol() + { + $object = new Marker(); + + $expected = array_rand(Marker::$arraySymbol); + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Marker', $object->setSymbol($expected)); + $this->assertEquals($expected, $object->getSymbol()); + } + + public function testSize() + { + $object = new Marker(); + $value = rand(1, 100); + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Marker', $object->setSize($value)); + $this->assertEquals($value, $object->getSize()); + } +} diff --git a/tests/PhpPresentation/Tests/Shape/Chart/SeriesTest.php b/tests/PhpPresentation/Tests/Shape/Chart/SeriesTest.php index a5f440437..34a76b69b 100644 --- a/tests/PhpPresentation/Tests/Shape/Chart/SeriesTest.php +++ b/tests/PhpPresentation/Tests/Shape/Chart/SeriesTest.php @@ -17,9 +17,11 @@ namespace PhpOffice\PhpPresentation\Tests\Shape\Chart; +use PhpOffice\PhpPresentation\Shape\Chart\Marker; use PhpOffice\PhpPresentation\Shape\Chart\Series; use PhpOffice\PhpPresentation\Style\Fill; use PhpOffice\PhpPresentation\Style\Font; +use PhpOffice\PhpPresentation\Style\Outline; /** * Test class for Series element @@ -39,6 +41,8 @@ public function testConstruct() $this->assertEquals('Series Title', $object->getTitle()); $this->assertInternalType('array', $object->getValues()); $this->assertEmpty($object->getValues()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Marker', $object->getMarker()); + $this->assertNull($object->getOutline()); } public function testDataLabelNumFormat() @@ -115,6 +119,22 @@ public function testLabelPosition() $this->assertEquals(Series::LABEL_INSIDEBASE, $object->getLabelPosition()); } + public function testMarker() + { + $object = new Series(); + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Series', $object->setMarker(new Marker())); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Marker', $object->getMarker()); + } + + public function testOutline() + { + $object = new Series(); + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Series', $object->setOutline(new Outline())); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Outline', $object->getOutline()); + } + public function testShowCategoryName() { $object = new Series(); diff --git a/tests/PhpPresentation/Tests/Shape/Chart/Type/AreaTest.php b/tests/PhpPresentation/Tests/Shape/Chart/Type/AreaTest.php index c1766c141..321d8a0e5 100644 --- a/tests/PhpPresentation/Tests/Shape/Chart/Type/AreaTest.php +++ b/tests/PhpPresentation/Tests/Shape/Chart/Type/AreaTest.php @@ -31,18 +31,18 @@ public function testData() { $object = new Area(); - $this->assertInternalType('array', $object->getData()); - $this->assertEmpty($object->getData()); + $this->assertInternalType('array', $object->getSeries()); + $this->assertEmpty($object->getSeries()); $array = array( new Series(), new Series(), ); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Area', $object->setData()); - $this->assertEmpty($object->getData()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Area', $object->setData($array)); - $this->assertCount(count($array), $object->getData()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Area', $object->setSeries()); + $this->assertEmpty($object->getSeries()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Area', $object->setSeries($array)); + $this->assertCount(count($array), $object->getSeries()); } public function testSeries() @@ -50,7 +50,7 @@ public function testSeries() $object = new Area(); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Area', $object->addSeries(new Series())); - $this->assertCount(1, $object->getData()); + $this->assertCount(1, $object->getSeries()); } public function testHashCode() diff --git a/tests/PhpPresentation/Tests/Shape/Chart/Type/Bar3DTest.php b/tests/PhpPresentation/Tests/Shape/Chart/Type/Bar3DTest.php index 021d3dc78..1853a1bad 100644 --- a/tests/PhpPresentation/Tests/Shape/Chart/Type/Bar3DTest.php +++ b/tests/PhpPresentation/Tests/Shape/Chart/Type/Bar3DTest.php @@ -31,18 +31,18 @@ public function testData() { $object = new Bar3D(); - $this->assertInternalType('array', $object->getData()); - $this->assertEmpty($object->getData()); + $this->assertInternalType('array', $object->getSeries()); + $this->assertEmpty($object->getSeries()); $array = array( new Series(), new Series(), ); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Bar3D', $object->setData()); - $this->assertEmpty($object->getData()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Bar3D', $object->setData($array)); - $this->assertCount(count($array), $object->getData()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Bar3D', $object->setSeries()); + $this->assertEmpty($object->getSeries()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Bar3D', $object->setSeries($array)); + $this->assertCount(count($array), $object->getSeries()); } public function testSeries() @@ -50,7 +50,7 @@ public function testSeries() $object = new Bar3D(); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Bar3D', $object->addSeries(new Series())); - $this->assertCount(1, $object->getData()); + $this->assertCount(1, $object->getSeries()); } public function testBarDirection() diff --git a/tests/PhpPresentation/Tests/Shape/Chart/Type/BarTest.php b/tests/PhpPresentation/Tests/Shape/Chart/Type/BarTest.php index eba91bbc9..302991811 100644 --- a/tests/PhpPresentation/Tests/Shape/Chart/Type/BarTest.php +++ b/tests/PhpPresentation/Tests/Shape/Chart/Type/BarTest.php @@ -31,18 +31,18 @@ public function testData() { $object = new Bar(); - $this->assertInternalType('array', $object->getData()); - $this->assertEmpty($object->getData()); + $this->assertInternalType('array', $object->getSeries()); + $this->assertEmpty($object->getSeries()); $array = array( new Series(), new Series(), ); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Bar', $object->setData()); - $this->assertEmpty($object->getData()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Bar', $object->setData($array)); - $this->assertCount(count($array), $object->getData()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Bar', $object->setSeries()); + $this->assertEmpty($object->getSeries()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Bar', $object->setSeries($array)); + $this->assertCount(count($array), $object->getSeries()); } public function testSeries() @@ -50,7 +50,7 @@ public function testSeries() $object = new Bar(); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Bar', $object->addSeries(new Series())); - $this->assertCount(1, $object->getData()); + $this->assertCount(1, $object->getSeries()); } public function testBarDirection() diff --git a/tests/PhpPresentation/Tests/Shape/Chart/Type/LineTest.php b/tests/PhpPresentation/Tests/Shape/Chart/Type/LineTest.php index 96471cb76..f8d0006a9 100644 --- a/tests/PhpPresentation/Tests/Shape/Chart/Type/LineTest.php +++ b/tests/PhpPresentation/Tests/Shape/Chart/Type/LineTest.php @@ -31,18 +31,18 @@ public function testData() { $object = new Line(); - $this->assertInternalType('array', $object->getData()); - $this->assertEmpty($object->getData()); + $this->assertInternalType('array', $object->getSeries()); + $this->assertEmpty($object->getSeries()); $array = array( new Series(), new Series(), ); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Line', $object->setData()); - $this->assertEmpty($object->getData()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Line', $object->setData($array)); - $this->assertCount(count($array), $object->getData()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Line', $object->setSeries()); + $this->assertEmpty($object->getSeries()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Line', $object->setSeries($array)); + $this->assertCount(count($array), $object->getSeries()); } public function testSerties() @@ -50,7 +50,7 @@ public function testSerties() $object = new Line(); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Line', $object->addSeries(new Series())); - $this->assertCount(1, $object->getData()); + $this->assertCount(1, $object->getSeries()); } public function testHashCode() diff --git a/tests/PhpPresentation/Tests/Shape/Chart/Type/Pie3DTest.php b/tests/PhpPresentation/Tests/Shape/Chart/Type/Pie3DTest.php index da3c96ac5..ebdf555cf 100644 --- a/tests/PhpPresentation/Tests/Shape/Chart/Type/Pie3DTest.php +++ b/tests/PhpPresentation/Tests/Shape/Chart/Type/Pie3DTest.php @@ -31,18 +31,18 @@ public function testData() { $object = new Pie3D(); - $this->assertInternalType('array', $object->getData()); - $this->assertEmpty($object->getData()); + $this->assertInternalType('array', $object->getSeries()); + $this->assertEmpty($object->getSeries()); $array = array( new Series(), new Series(), ); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Pie3D', $object->setData()); - $this->assertEmpty($object->getData()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Pie3D', $object->setData($array)); - $this->assertCount(count($array), $object->getData()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Pie3D', $object->setSeries()); + $this->assertEmpty($object->getSeries()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Pie3D', $object->setSeries($array)); + $this->assertCount(count($array), $object->getSeries()); } public function testSeries() @@ -50,7 +50,7 @@ public function testSeries() $object = new Pie3D(); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Pie3D', $object->addSeries(new Series())); - $this->assertCount(1, $object->getData()); + $this->assertCount(1, $object->getSeries()); } public function testExplosion() diff --git a/tests/PhpPresentation/Tests/Shape/Chart/Type/PieTest.php b/tests/PhpPresentation/Tests/Shape/Chart/Type/PieTest.php index d4a7b4661..a0d77e8cd 100644 --- a/tests/PhpPresentation/Tests/Shape/Chart/Type/PieTest.php +++ b/tests/PhpPresentation/Tests/Shape/Chart/Type/PieTest.php @@ -31,18 +31,18 @@ public function testData() { $object = new Pie(); - $this->assertInternalType('array', $object->getData()); - $this->assertEmpty($object->getData()); + $this->assertInternalType('array', $object->getSeries()); + $this->assertEmpty($object->getSeries()); $array = array( new Series(), new Series(), ); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Pie', $object->setData()); - $this->assertEmpty($object->getData()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Pie', $object->setData($array)); - $this->assertCount(count($array), $object->getData()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Pie', $object->setSeries()); + $this->assertEmpty($object->getSeries()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Pie', $object->setSeries($array)); + $this->assertCount(count($array), $object->getSeries()); } public function testSeries() @@ -50,7 +50,7 @@ public function testSeries() $object = new Pie(); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Pie', $object->addSeries(new Series())); - $this->assertCount(1, $object->getData()); + $this->assertCount(1, $object->getSeries()); } diff --git a/tests/PhpPresentation/Tests/Shape/Chart/Type/ScatterTest.php b/tests/PhpPresentation/Tests/Shape/Chart/Type/ScatterTest.php index 264658e0d..f74c92091 100644 --- a/tests/PhpPresentation/Tests/Shape/Chart/Type/ScatterTest.php +++ b/tests/PhpPresentation/Tests/Shape/Chart/Type/ScatterTest.php @@ -31,18 +31,18 @@ public function testData() { $object = new Scatter(); - $this->assertInternalType('array', $object->getData()); - $this->assertEmpty($object->getData()); + $this->assertInternalType('array', $object->getSeries()); + $this->assertEmpty($object->getSeries()); $array = array( new Series(), new Series(), ); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Scatter', $object->setData()); - $this->assertEmpty($object->getData()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Scatter', $object->setData($array)); - $this->assertCount(count($array), $object->getData()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Scatter', $object->setSeries()); + $this->assertEmpty($object->getSeries()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Scatter', $object->setSeries($array)); + $this->assertCount(count($array), $object->getSeries()); } public function testSerties() @@ -50,7 +50,7 @@ public function testSerties() $object = new Scatter(); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart\\Type\\Scatter', $object->addSeries(new Series())); - $this->assertCount(1, $object->getData()); + $this->assertCount(1, $object->getSeries()); } public function testHashCode() diff --git a/tests/PhpPresentation/Tests/Shape/Comment/AuthorTest.php b/tests/PhpPresentation/Tests/Shape/Comment/AuthorTest.php new file mode 100644 index 000000000..cc4afc082 --- /dev/null +++ b/tests/PhpPresentation/Tests/Shape/Comment/AuthorTest.php @@ -0,0 +1,67 @@ +assertNull($object->getName()); + $this->assertNull($object->getIndex()); + $this->assertNull($object->getInitials()); + } + + public function testGetSetIndex() + { + $expectedVal = rand(1, 100); + + $object = new Author(); + $this->assertNull($object->getIndex()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Comment\\Author', $object->setIndex($expectedVal)); + $this->assertEquals($expectedVal, $object->getIndex()); + } + + public function testGetSetInitials() + { + $expectedVal = 'AABBCCDD'; + + $object = new Author(); + $this->assertNull($object->getInitials()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Comment\\Author', $object->setInitials($expectedVal)); + $this->assertEquals($expectedVal, $object->getInitials()); + } + + public function testGetSetName() + { + $expectedVal = 'AABBCCDD'; + + $object = new Author(); + $this->assertNull($object->getName()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Comment\\Author', $object->setName($expectedVal)); + $this->assertEquals($expectedVal, $object->getName()); + } +} diff --git a/tests/PhpPresentation/Tests/Shape/CommentTest.php b/tests/PhpPresentation/Tests/Shape/CommentTest.php new file mode 100644 index 000000000..8223b7a6e --- /dev/null +++ b/tests/PhpPresentation/Tests/Shape/CommentTest.php @@ -0,0 +1,82 @@ +assertNull($object->getAuthor()); + $this->assertNull($object->getText()); + $this->assertInternalType('int', $object->getDate()); + $this->assertNull($object->getHeight()); + $this->assertNull($object->getWidth()); + } + + public function testGetSetAuthor() + { + $object = new Comment(); + + $oStub = $this->getMock('PhpOffice\PhpPresentation\Shape\Comment\Author'); + + $this->assertNull($object->getAuthor()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Comment', $object->setAuthor($oStub)); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Comment\\Author', $object->getAuthor()); + } + + public function testGetSetDate() + { + $expectedDate = time(); + + $object = new Comment(); + $this->assertInternalType('int', $object->getDate()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Comment', $object->setDate($expectedDate)); + $this->assertEquals($expectedDate, $object->getDate()); + $this->assertInternalType('int', $object->getDate()); + } + + public function testGetSetText() + { + $expectedText = 'AABBCCDD'; + + $object = new Comment(); + $this->assertNull($object->getText()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Comment', $object->setText($expectedText)); + $this->assertEquals($expectedText, $object->getText()); + } + + public function testGetSetHeightAndWidtg() + { + $object = new Comment(); + $this->assertNull($object->getHeight()); + $this->assertNull($object->getWidth()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Comment', $object->setHeight(1)); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Comment', $object->setWidth(1)); + $this->assertNull($object->getHeight()); + $this->assertNull($object->getWidth()); + } +} diff --git a/tests/PhpPresentation/Tests/Shape/Drawing/Base64Test.php b/tests/PhpPresentation/Tests/Shape/Drawing/Base64Test.php new file mode 100644 index 000000000..90538779b --- /dev/null +++ b/tests/PhpPresentation/Tests/Shape/Drawing/Base64Test.php @@ -0,0 +1,66 @@ +assertNull($oDrawing->getData()); + $oDrawing->setData($this->imageData); + $this->assertNotEmpty($oDrawing->getData()); + } + + public function testExtension() + { + $oDrawing = new Base64(); + $oDrawing->setData($this->imageData); + $this->assertEquals('jpg', $oDrawing->getExtension()); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Type Mime not found : "fake/fake" + */ + public function testExtensionException() + { + $imgData = str_replace('image/jpeg', 'fake/fake', $this->imageData); + + $oDrawing = new Base64(); + $oDrawing->setData($imgData); + $oDrawing->getExtension(); + } + + public function testMimeType() + { + $oDrawing = new Base64(); + $oDrawing->setData($this->imageData); + $this->assertEquals('image/jpeg', $oDrawing->getMimeType()); + } +} diff --git a/tests/PhpPresentation/Tests/Shape/Drawing/FileTest.php b/tests/PhpPresentation/Tests/Shape/Drawing/FileTest.php new file mode 100644 index 000000000..75cdeda96 --- /dev/null +++ b/tests/PhpPresentation/Tests/Shape/Drawing/FileTest.php @@ -0,0 +1,64 @@ +assertEmpty($object->getPath()); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage File not found! + */ + public function testPathBasic() + { + $object = new File(); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing\\File', $object->setPath()); + } + + public function testPathWithoutVerifyFile() + { + $object = new File(); + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing\\File', $object->setPath('', false)); + $this->assertEmpty($object->getPath()); + } + + public function testPathWithRealFile() + { + $object = new File(); + + $imagePath = PHPPRESENTATION_TESTS_BASE_DIR.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.'PhpPresentationLogo.png'; + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing\\File', $object->setPath($imagePath, false)); + $this->assertEquals($imagePath, $object->getPath()); + $this->assertEquals(0, $object->getWidth()); + $this->assertEquals(0, $object->getHeight()); + } +} diff --git a/tests/PhpPresentation/Tests/Shape/Drawing/ZipFileTest.php b/tests/PhpPresentation/Tests/Shape/Drawing/ZipFileTest.php new file mode 100644 index 000000000..0c4539b3b --- /dev/null +++ b/tests/PhpPresentation/Tests/Shape/Drawing/ZipFileTest.php @@ -0,0 +1,66 @@ +fileOk = 'zip://'.PHPPRESENTATION_TESTS_BASE_DIR.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'files'.DIRECTORY_SEPARATOR.'Sample_01_Simple.pptx#ppt/media/phppowerpoint_logo1.gif'; + $this->fileKoZip = 'zip://'.PHPPRESENTATION_TESTS_BASE_DIR.DIRECTORY_SEPARATOR.'fileNotExist.pptx#ppt/media/phppowerpoint_logo1.gif'; + $this->fileKoFile = 'zip://'.PHPPRESENTATION_TESTS_BASE_DIR.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'files'.DIRECTORY_SEPARATOR.'Sample_01_Simple.pptx#ppt/media/filenotexists.gif'; + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage fileNotExist.pptx does not exist + */ + public function testContentsException() + { + $oDrawing = new ZipFile(); + $oDrawing->setPath($this->fileKoZip); + $oDrawing->getContents(); + } + + public function testExtension() + { + $oDrawing = new ZipFile(); + $oDrawing->setPath($this->fileOk); + $this->assertEquals('gif', $oDrawing->getExtension()); + } + + public function testMimeType() + { + $oDrawing = new ZipFile(); + $oDrawing->setPath($this->fileOk); + $this->assertEquals('image/gif', $oDrawing->getMimeType()); + } +} diff --git a/tests/PhpPresentation/Tests/Shape/DrawingTest.php b/tests/PhpPresentation/Tests/Shape/DrawingTest.php deleted file mode 100644 index 118fd699f..000000000 --- a/tests/PhpPresentation/Tests/Shape/DrawingTest.php +++ /dev/null @@ -1,159 +0,0 @@ -assertEmpty($object->getPath()); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage File not found! - */ - public function testPathBasic() - { - $object = new Drawing(); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setPath()); - } - - public function testPathWithoutVerifyFile() - { - $object = new Drawing(); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setPath('', false)); - $this->assertEmpty($object->getPath()); - } - - public function testPathWithRealFile() - { - $object = new Drawing(); - - $imagePath = PHPPRESENTATION_TESTS_BASE_DIR.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.'PhpPresentationLogo.png'; - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setPath($imagePath, false)); - $this->assertEquals($imagePath, $object->getPath()); - $this->assertEquals(0, $object->getWidth()); - $this->assertEquals(0, $object->getHeight()); - } - - public function testGetSetDescription() - { - $object = new Drawing(); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setDescription()); - $this->assertEmpty($object->getDescription()); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setDescription('TEST')); - $this->assertEquals('TEST', $object->getDescription()); - } - - public function testGetSetResizeProportional() - { - $object = new Drawing(); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setResizeProportional()); - $this->assertTrue($object->isResizeProportional()); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setResizeProportional(false)); - $this->assertFalse($object->isResizeProportional()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setResizeProportional(true)); - $this->assertTrue($object->isResizeProportional()); - } - - public function testGetSetHeightWidth() - { - $object = new Drawing(); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setWidth()); - $this->assertEquals(0, $object->getWidth()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setHeight()); - $this->assertEquals(0, $object->getHeight()); - } - /** - * Value : Resize Proportional (false) - */ - public function testGetSetHeightWidthResizeProportionalFalse() - { - // Valeur : Resize Proportional (false) - $object = new Drawing(); - $value = rand(0, 100); - $object->setResizeProportional(false); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setWidth($value)); - $this->assertEquals($value, $object->getWidth()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setHeight($value)); - $this->assertEquals($value, $object->getHeight()); - } - - /** - * Value : Resize Proportional (true) - */ - public function testGetSetHeightWidthResizeProportionalTrue() - { - $object = new Drawing(); - $valueWidth = rand(1, 100); - $valueHeight = rand(1, 100); - $object->setResizeProportional(false); - $object->setWidth($valueWidth); - $object->setHeight($valueHeight); - $object->setResizeProportional(true); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setWidth($valueWidth)); - $this->assertEquals($valueWidth, $object->getWidth()); - $this->assertEquals(round($valueWidth * ($valueHeight / $valueWidth)), $object->getHeight()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setHeight($valueHeight)); - $this->assertEquals($valueHeight, $object->getHeight()); - $this->assertEquals(round($valueHeight * ($valueWidth / $valueHeight)), $object->getWidth()); - } - - public function testSetWidthAndHeight() - { - $object = new Drawing(); - $valueWidth = rand(1, 100); - $valueHeight = $valueWidth / 2; - $object->setResizeProportional(false); - $object->setWidth($valueWidth); - $object->setHeight($valueHeight); - $object->setResizeProportional(true); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setWidthAndHeight($valueHeight, $valueWidth)); - $this->assertEquals($valueHeight, $object->getWidth()); - $this->assertEquals(ceil($valueHeight * ($valueHeight/$valueWidth)), $object->getHeight()); - - $object = new Drawing(); - $valueWidth = rand(1, 100); - $valueHeight = $valueWidth / 2; - $object->setResizeProportional(false); - $object->setWidth($valueWidth); - $object->setHeight($valueHeight); - $object->setResizeProportional(true); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->setWidthAndHeight($valueWidth, $valueHeight)); - $this->assertEquals($valueHeight, $object->getHeight()); - $this->assertEquals(ceil($valueWidth * ($valueHeight/$valueHeight)), $object->getWidth()); - } -} diff --git a/tests/PhpPresentation/Tests/Shape/GroupTest.php b/tests/PhpPresentation/Tests/Shape/GroupTest.php index 77ab387a0..959e7ac31 100644 --- a/tests/PhpPresentation/Tests/Shape/GroupTest.php +++ b/tests/PhpPresentation/Tests/Shape/GroupTest.php @@ -45,7 +45,7 @@ public function testAdd() $object = new Group(); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Chart', $object->createChartShape()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing', $object->createDrawingShape()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing\\File', $object->createDrawingShape()); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Line', $object->createLineShape(10, 10, 10, 10)); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\RichText', $object->createRichTextShape()); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Table', $object->createTableShape()); diff --git a/tests/PhpPresentation/Tests/Shape/MediaTest.php b/tests/PhpPresentation/Tests/Shape/MediaTest.php new file mode 100644 index 000000000..ef634299e --- /dev/null +++ b/tests/PhpPresentation/Tests/Shape/MediaTest.php @@ -0,0 +1,28 @@ +assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Drawing\\File', $object); + } + + public function testMimeType() + { + $object = new Media(); + $object->setPath('file.mp4', false); + $this->assertEquals('video/mp4', $object->getMimeType()); + $object->setPath('file.ogv', false); + $this->assertEquals('video/ogg', $object->getMimeType()); + $object->setPath('file.wmv', false); + $this->assertEquals('video/x-ms-wmv', $object->getMimeType()); + $object->setPath('file.xxx', false); + $this->assertEquals('application/octet-stream', $object->getMimeType()); + } +} diff --git a/tests/PhpPresentation/Tests/Shape/MemoryDrawingTest.php b/tests/PhpPresentation/Tests/Shape/MemoryDrawingTest.php deleted file mode 100644 index d4d7a9c6e..000000000 --- a/tests/PhpPresentation/Tests/Shape/MemoryDrawingTest.php +++ /dev/null @@ -1,83 +0,0 @@ -assertEquals('imagepng', $object->getRenderingFunction()); - $this->assertEquals(MemoryDrawing::MIMETYPE_DEFAULT, $object->getMimeType()); - $this->assertNull($object->getImageResource()); - $this->assertInternalType('string', $object->getIndexedFilename()); - $this->assertInternalType('string', $object->getExtension()); - $this->assertInternalType('string', $object->getHashCode()); - } - - public function testImageResource() - { - $object = new MemoryDrawing(); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\MemoryDrawing', $object->setImageResource()); - $this->assertNull($object->getImageResource()); - $this->assertEquals(0, $object->getWidth()); - $this->assertEquals(0, $object->getHeight()); - - $width = rand(1, 100); - $height = rand(100, 200); - $gdImage = @imagecreatetruecolor($width, $height); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\MemoryDrawing', $object->setImageResource($gdImage)); - $this->assertTrue(is_resource($object->getImageResource())); - $this->assertEquals($width, $object->getWidth()); - $this->assertEquals($height, $object->getHeight()); - } - - public function testMimeType() - { - $object = new MemoryDrawing(); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\MemoryDrawing', $object->setMimeType()); - $this->assertEquals(MemoryDrawing::MIMETYPE_DEFAULT, $object->getMimeType()); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\MemoryDrawing', $object->setMimeType(MemoryDrawing::MIMETYPE_JPEG)); - $this->assertEquals(MemoryDrawing::MIMETYPE_JPEG, $object->getMimeType()); - } - - public function testRenderingFunction() - { - $object = new MemoryDrawing(); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\MemoryDrawing', $object->setRenderingFunction()); - $this->assertEquals(MemoryDrawing::RENDERING_DEFAULT, $object->getRenderingFunction()); - - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\MemoryDrawing', $object->setRenderingFunction(MemoryDrawing::RENDERING_JPEG)); - $this->assertEquals(MemoryDrawing::RENDERING_JPEG, $object->getRenderingFunction()); - } -} diff --git a/tests/PhpPresentation/Tests/Shape/RichText/BreakElementTest.php b/tests/PhpPresentation/Tests/Shape/RichText/BreakElementTest.php index 6fb7f598b..6a03b335a 100644 --- a/tests/PhpPresentation/Tests/Shape/RichText/BreakElementTest.php +++ b/tests/PhpPresentation/Tests/Shape/RichText/BreakElementTest.php @@ -45,6 +45,14 @@ public function testFont() $this->assertNull($object->getFont()); } + public function testLanguage() + { + $object = new BreakElement(); + $this->assertNull($object->getLanguage()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\RichText\\BreakElement', $object->setLanguage('en-US')); + $this->assertNull($object->getLanguage()); + } + /** * Test get/set hash index */ diff --git a/tests/PhpPresentation/Tests/Shape/RichText/ParagraphTest.php b/tests/PhpPresentation/Tests/Shape/RichText/ParagraphTest.php index 94901999d..4ab5ca6cf 100644 --- a/tests/PhpPresentation/Tests/Shape/RichText/ParagraphTest.php +++ b/tests/PhpPresentation/Tests/Shape/RichText/ParagraphTest.php @@ -97,6 +97,18 @@ public function testHashIndex() $this->assertEquals($value, $object->getHashIndex()); } + /** + * Test get/set linespacing + */ + public function testLineSpacing() + { + $object = new Paragraph(); + $valueExpected = rand(1, 100); + $this->assertEquals(100, $object->getLineSpacing()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\RichText\\Paragraph', $object->setLineSpacing($valueExpected)); + $this->assertEquals($valueExpected, $object->getLineSpacing()); + } + /** * Test get/set richTextElements */ diff --git a/tests/PhpPresentation/Tests/Shape/RichText/RunTest.php b/tests/PhpPresentation/Tests/Shape/RichText/RunTest.php index 2739d9929..8dda48d65 100644 --- a/tests/PhpPresentation/Tests/Shape/RichText/RunTest.php +++ b/tests/PhpPresentation/Tests/Shape/RichText/RunTest.php @@ -48,6 +48,14 @@ public function testFont() $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Font', $object->getFont()); } + public function testLanguage() + { + $object = new Run(); + $this->assertNull($object->getLanguage()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\RichText\\Run', $object->setLanguage('en-US')); + $this->assertEquals('en-US', $object->getLanguage()); + } + public function testText() { $object = new Run(); diff --git a/tests/PhpPresentation/Tests/Shape/RichText/TextElementTest.php b/tests/PhpPresentation/Tests/Shape/RichText/TextElementTest.php index 0ceafb787..4a9034cf8 100644 --- a/tests/PhpPresentation/Tests/Shape/RichText/TextElementTest.php +++ b/tests/PhpPresentation/Tests/Shape/RichText/TextElementTest.php @@ -58,6 +58,14 @@ public function testHyperlink() $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Hyperlink', $object->getHyperlink()); } + public function testLanguage() + { + $object = new TextElement(); + $this->assertNull($object->getLanguage()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\RichText\\TextElement', $object->setLanguage('en-US')); + $this->assertEquals('en-US', $object->getLanguage()); + } + public function testText() { $object = new TextElement(); diff --git a/tests/PhpPresentation/Tests/Slide/AnimationTest.php b/tests/PhpPresentation/Tests/Slide/AnimationTest.php new file mode 100644 index 000000000..0a1a065e3 --- /dev/null +++ b/tests/PhpPresentation/Tests/Slide/AnimationTest.php @@ -0,0 +1,47 @@ +getMockForAbstractClass('PhpOffice\PhpPresentation\AbstractShape'); + + $object = new Animation(); + + $this->assertInternalType('array', $object->getShapeCollection()); + $this->assertCount(0, $object->getShapeCollection()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\Animation', $object->addShape($oStub)); + $this->assertInternalType('array', $object->getShapeCollection()); + $this->assertCount(1, $object->getShapeCollection()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\Animation', $object->setShapeCollection()); + $this->assertInternalType('array', $object->getShapeCollection()); + $this->assertCount(0, $object->getShapeCollection()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\Animation', $object->setShapeCollection(array($oStub))); + $this->assertInternalType('array', $object->getShapeCollection()); + $this->assertCount(1, $object->getShapeCollection()); + } +} diff --git a/tests/PhpPresentation/Tests/Slide/SlideLayoutTest.php b/tests/PhpPresentation/Tests/Slide/SlideLayoutTest.php new file mode 100644 index 000000000..eab73ea48 --- /dev/null +++ b/tests/PhpPresentation/Tests/Slide/SlideLayoutTest.php @@ -0,0 +1,53 @@ +getMockForAbstractClass('PhpOffice\PhpPresentation\Slide\SlideMaster'); + + $object = new SlideLayout($mockSlideMaster); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\AbstractSlide', $object); + $this->assertInstanceOf('\\ArrayObject', $object->getShapeCollection()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\ColorMap', $object->colorMap); + } + + public function testLayoutName() + { + // Mocks + $mockSlideMaster = $this->getMockForAbstractClass('PhpOffice\PhpPresentation\Slide\SlideMaster'); + + // Expected + $expectedLayoutName = 'Title'.rand(1, 100); + + $object = new SlideLayout($mockSlideMaster); + + $this->assertNull($object->getLayoutName()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\SlideLayout', $object->setLayoutName($expectedLayoutName)); + $this->assertEquals($expectedLayoutName, $object->getLayoutName()); + } +} diff --git a/tests/PhpPresentation/Tests/Slide/SlideMasterTest.php b/tests/PhpPresentation/Tests/Slide/SlideMasterTest.php new file mode 100644 index 000000000..ae44d2afa --- /dev/null +++ b/tests/PhpPresentation/Tests/Slide/SlideMasterTest.php @@ -0,0 +1,87 @@ +assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\AbstractSlide', $object); + $this->assertNull($object->getParent()); + $this->assertInstanceOf('\\ArrayObject', $object->getShapeCollection()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\ColorMap', $object->colorMap); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\Background\\Color', $object->getBackground()); + $this->assertEquals('FFFFFF', $object->getBackground()->getColor()->getRGB()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\TextStyle', $object->getTextStyles()); + } + + public function testLayout() + { + $object = new SlideMaster(); + + // Mock Post + $mockSlideLayout = $this->getMockForAbstractClass('PhpOffice\PhpPresentation\Slide\SlideLayout', array($object)); + + $this->assertEmpty($object->getAllSlideLayouts()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\SlideLayout', $object->createSlideLayout()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\SlideLayout', $object->addSlideLayout($mockSlideLayout)); + $this->assertCount(2, $object->getAllSlideLayouts()); + } + + public function testSchemeColors() + { + // Mock Pre + $mockSchemeColorAccent1 = $this->getMockForAbstractClass('PhpOffice\PhpPresentation\Style\SchemeColor'); + $mockSchemeColorAccent1->setValue('accent1'); + $mockSchemeColorAccent1->setRGB('ABCDEF'); + $mockSchemeColorNew = $this->getMockForAbstractClass('PhpOffice\PhpPresentation\Style\SchemeColor'); + $mockSchemeColorNew->setValue('new'); + $mockSchemeColorNew->setRGB('ABCDEF'); + + $object = new SlideMaster(); + + $this->assertInternalType('array', $object->getAllSchemeColors()); + $this->assertCount(12, $object->getAllSchemeColors()); + // Add idem value + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\SlideMaster', $object->addSchemeColor($mockSchemeColorAccent1)); + $this->assertCount(12, $object->getAllSchemeColors()); + // Add new value + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\SlideMaster', $object->addSchemeColor($mockSchemeColorNew)); + $this->assertCount(13, $object->getAllSchemeColors()); + } + + public function testTextStyles() + { + // Mock Pre + $mockTextStyle = $this->getMockForAbstractClass('PhpOffice\PhpPresentation\Style\TextStyle'); + + $object = new SlideMaster(); + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\TextStyle', $object->getTextStyles()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\SlideMaster', $object->setTextStyles($mockTextStyle)); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\TextStyle', $object->getTextStyles()); + } +} diff --git a/tests/PhpPresentation/Tests/Slide/TransitionTest.php b/tests/PhpPresentation/Tests/Slide/TransitionTest.php index 4b1b56dad..31dace8b2 100644 --- a/tests/PhpPresentation/Tests/Slide/TransitionTest.php +++ b/tests/PhpPresentation/Tests/Slide/TransitionTest.php @@ -88,6 +88,5 @@ public function testTransitionType() $this->assertNull($object->getTransitionType()); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\Transition', $object->setTransitionType(Transition::TRANSITION_RANDOM)); $this->assertEquals(Transition::TRANSITION_RANDOM, $object->getTransitionType()); - } } diff --git a/tests/PhpPresentation/Tests/SlideTest.php b/tests/PhpPresentation/Tests/SlideTest.php index 3af785fe0..3d967e740 100644 --- a/tests/PhpPresentation/Tests/SlideTest.php +++ b/tests/PhpPresentation/Tests/SlideTest.php @@ -56,16 +56,6 @@ public function testParent() $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $object->getParent()); } - public function testSlideLayout() - { - $object = new Slide(); - $this->assertEquals(Slide\Layout::BLANK, $object->getSlideLayout()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide', $object->setSlideLayout()); - $this->assertEquals(Slide\Layout::BLANK, $object->getSlideLayout()); - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide', $object->setSlideLayout(Slide\Layout::TITLE_SLIDE)); - $this->assertEquals(Slide\Layout::TITLE_SLIDE, $object->getSlideLayout()); - } - public function testSlideMasterId() { $value = rand(1, 100); @@ -78,6 +68,24 @@ public function testSlideMasterId() $this->assertEquals($value, $object->getSlideMasterId()); } + public function testAnimations() + { + $oStub = $this->getMockForAbstractClass('PhpOffice\PhpPresentation\Slide\Animation'); + + $object = new Slide(); + $this->assertInternalType('array', $object->getAnimations()); + $this->assertCount(0, $object->getAnimations()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide', $object->addAnimation($oStub)); + $this->assertInternalType('array', $object->getAnimations()); + $this->assertCount(1, $object->getAnimations()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide', $object->setAnimations()); + $this->assertInternalType('array', $object->getAnimations()); + $this->assertCount(0, $object->getAnimations()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide', $object->setAnimations(array($oStub))); + $this->assertInternalType('array', $object->getAnimations()); + $this->assertCount(1, $object->getAnimations()); + } + public function testBackground() { $oStub = $this->getMockForAbstractClass('PhpOffice\PhpPresentation\Slide\AbstractBackground'); @@ -118,4 +126,14 @@ public function testTransition() $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide', $object->setTransition(null)); $this->assertNull($object->getTransition()); } + + public function testVisible() + { + $object = new Slide(); + $this->assertTrue($object->isVisible()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide', $object->setIsVisible(false)); + $this->assertFalse($object->isVisible()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Slide', $object->setIsVisible()); + $this->assertTrue($object->isVisible()); + } } diff --git a/tests/PhpPresentation/Tests/Style/BulletTest.php b/tests/PhpPresentation/Tests/Style/BulletTest.php index 5fb0ccb72..468c23273 100644 --- a/tests/PhpPresentation/Tests/Style/BulletTest.php +++ b/tests/PhpPresentation/Tests/Style/BulletTest.php @@ -18,6 +18,7 @@ namespace PhpOffice\PhpPresentation\Tests\Style; use PhpOffice\PhpPresentation\Style\Bullet; +use PhpOffice\PhpPresentation\Style\Color; /** * Test class for PhpPresentation @@ -51,6 +52,25 @@ public function testSetGetBulletChar() $this->assertEquals('a', $object->getBulletChar()); } + /** + * Test get/set bullet color + */ + public function testSetGetBulletColor() + { + $object = new Bullet(); + + $expectedARGB = '01234567'; + + // default + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Color', $object->getBulletColor()); + $this->assertEquals(Color::COLOR_BLACK, $object->getBulletColor()->getARGB()); + + + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Bullet', $object->setBulletColor(new Color($expectedARGB))); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Color', $object->getBulletColor()); + $this->assertEquals($expectedARGB, $object->getBulletColor()->getARGB()); + } + /** * Test get/set bullet font */ diff --git a/tests/PhpPresentation/Tests/Style/ColorTest.php b/tests/PhpPresentation/Tests/Style/ColorTest.php index c38b35204..672b0e27b 100644 --- a/tests/PhpPresentation/Tests/Style/ColorTest.php +++ b/tests/PhpPresentation/Tests/Style/ColorTest.php @@ -37,6 +37,20 @@ public function testConstruct() $this->assertEquals(Color::COLOR_BLUE, $object->getARGB()); } + /** + * Test Alpha + */ + public function testAlpha() + { + $object = new Color(); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Color', $object->setARGB()); + $this->assertEquals(100, $object->getAlpha()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Color', $object->setARGB(Color::COLOR_BLUE)); + $this->assertEquals(100, $object->getAlpha()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Color', $object->setARGB('AA0000FF')); + $this->assertEquals(66.67, $object->getAlpha()); + } + /** * Test get/set ARGB */ diff --git a/tests/PhpPresentation/Tests/Style/FontTest.php b/tests/PhpPresentation/Tests/Style/FontTest.php index 9da614fdc..ba064fc89 100644 --- a/tests/PhpPresentation/Tests/Style/FontTest.php +++ b/tests/PhpPresentation/Tests/Style/FontTest.php @@ -47,12 +47,22 @@ public function testConstruct() /** * Test get/set color + * @expectedException \Exception + * @expectedExceptionMessage $pValue must be an instance of \PhpOffice\PhpPresentation\Style\Color */ - public function testSetGetColor() + public function testSetGetColorException() { $object = new Font(); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Font', $object->setColor()); - $this->assertNull($object->getColor()); + } + + /** + * Test get/set color + */ + public function testSetGetColor() + { + $object = new Font(); + $this->assertEquals(Color::COLOR_BLACK, $object->getColor()->getARGB()); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Font', $object->setColor(new Color(Color::COLOR_BLUE))); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Color', $object->getColor()); $this->assertEquals(Color::COLOR_BLUE, $object->getColor()->getARGB()); diff --git a/tests/PhpPresentation/Tests/Style/OutlineTest.php b/tests/PhpPresentation/Tests/Style/OutlineTest.php new file mode 100644 index 000000000..e7cdc8900 --- /dev/null +++ b/tests/PhpPresentation/Tests/Style/OutlineTest.php @@ -0,0 +1,64 @@ +assertNull($object->getWidth()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Fill', $object->getFill()); + } + + /** + * Test get/set fill + */ + public function testSetGetFill() + { + $object = new Outline(); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Fill', $object->getFill()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Outline', $object->setFill(new Fill())); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Fill', $object->getFill()); + } + + /** + * Test get/set width + */ + public function testSetGetWidth() + { + $object = new Outline(); + $this->assertNull($object->getWidth()); + $value = rand(1, 100); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Outline', $object->setWidth($value)); + $this->assertEquals($value, $object->getWidth()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Outline', $object->setWidth(1.5)); + $this->assertEquals(1, $object->getWidth()); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/AbstractWriterTest.php b/tests/PhpPresentation/Tests/Writer/AbstractWriterTest.php new file mode 100644 index 000000000..aad18a6bf --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/AbstractWriterTest.php @@ -0,0 +1,39 @@ +getMockForAbstractClass('PhpOffice\PhpPresentation\Writer\AbstractWriter'); + $oStubZip = $this->getMockForAbstractClass('PhpOffice\Common\Adapter\Zip\ZipInterface'); + + $this->assertNull($oStubWriter->getZipAdapter()); + $this->assertInstanceOf('PhpOffice\\PhpPresentation\\Writer\\AbstractWriter', $oStubWriter->setZipAdapter($oStubZip)); + $this->assertInstanceOf('PhpOffice\\Common\\Adapter\\Zip\\ZipInterface', $oStubWriter->getZipAdapter()); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/ODPresentation/AbstractPartTest.php b/tests/PhpPresentation/Tests/Writer/ODPresentation/AbstractPartTest.php deleted file mode 100644 index c0c353c3c..000000000 --- a/tests/PhpPresentation/Tests/Writer/ODPresentation/AbstractPartTest.php +++ /dev/null @@ -1,89 +0,0 @@ -setAccessible(true); - return $method->invokeArgs($obj, $args); - } - - /** - * Executed before each method of the class - */ - public function tearDown() - { - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage No parent \PhpOffice\PhpPresentation\Writer\WriterInterface assigned. - */ - public function testParentException() - { - $oDrawing = new Drawing(); - $oDrawing->getParentWriter(); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\ODPresentation - */ - public function testWriterException() - { - $oManifest = new Manifest(); - $oManifest->setParentWriter(new PowerPoint2007()); - $oManifest->writePart(); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\ODPresentation - */ - public function testXMLWriterException() - { - $oManifest = new Manifest(); - $oManifest->setParentWriter(new PowerPoint2007()); - $this->runProtectedMethod($oManifest, 'getXMLWriter'); - } - - public function testXMLWriterWithDiskCaching() - { - $oODPresentation = new ODPresentation(); - $oODPresentation->setUseDiskCaching(true); - $oManifest = new Manifest(); - $oManifest->setParentWriter($oODPresentation); - - $this->assertNotEmpty(\PHPUnit_Framework_Assert::readAttribute($this->runProtectedMethod($oManifest, 'getXMLWriter'), 'tempFileName')); - } -} diff --git a/tests/PhpPresentation/Tests/Writer/ODPresentation/ChartAreaTest.php b/tests/PhpPresentation/Tests/Writer/ODPresentation/ChartAreaTest.php deleted file mode 100644 index d05ab9cc8..000000000 --- a/tests/PhpPresentation/Tests/Writer/ODPresentation/ChartAreaTest.php +++ /dev/null @@ -1,76 +0,0 @@ - 1, 'Feb' => 5, 'Mar' => 2)); - $oSeries->getFill()->setStartColor(new Color('FF93A9CE')); - - $oArea = new Area(); - $oArea->addSeries($oSeries); - - $phpPresentation = new PhpPresentation(); - $oSlide = $phpPresentation->getActiveSlide(); - $oChart = $oSlide->createChartShape(); - $oChart->getPlotArea()->setType($oArea); - - $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); - - $element = '/office:document-content/office:body/office:chart/chart:chart'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('chart:area', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); - - $element = '/office:document-content/office:body/office:chart/chart:chart/chart:plot-area/chart:series'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('chart:area', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); - - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleSeries0\']/style:graphic-properties'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertFalse($pres->attributeElementExists($element, 'draw:fill', 'Object 1/content.xml')); - $this->assertTrue($pres->attributeElementExists($element, 'draw:fill-color', 'Object 1/content.xml')); - $this->assertEquals('#93A9CE', $pres->getElementAttribute($element, 'draw:fill-color', 'Object 1/content.xml')); - } -} diff --git a/tests/PhpPresentation/Tests/Writer/ODPresentation/ChartBarTest.php b/tests/PhpPresentation/Tests/Writer/ODPresentation/ChartBarTest.php deleted file mode 100644 index e4221b00b..000000000 --- a/tests/PhpPresentation/Tests/Writer/ODPresentation/ChartBarTest.php +++ /dev/null @@ -1,104 +0,0 @@ - 1, 'Feb' => 5, 'Mar' => 2)); - $oSeries->setShowSeriesName(true); - $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF4672A8')); - $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FFAB4744')); - $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF8AA64F')); - - $oBar = new Bar(); - $oBar->addSeries($oSeries); - - $phpPresentation = new PhpPresentation(); - $oSlide = $phpPresentation->getActiveSlide(); - $oChart = $oSlide->createChartShape(); - $oChart->getPlotArea()->setType($oBar); - - $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); - - $element = '/office:document-content/office:body/office:chart/chart:chart'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('chart:bar', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); - - $element = '/office:document-content/office:body/office:chart/chart:chart/chart:plot-area/chart:series/chart:data-point'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'stylePlotArea\']/style:chart-properties'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('false', $pres->getElementAttribute($element, 'chart:vertical', 'Object 1/content.xml')); - $this->assertFalse($pres->attributeElementExists($element, 'chart:three-dimensional', 'Object 1/content.xml')); - $this->assertFalse($pres->attributeElementExists($element, 'chart:right-angled-axes', 'Object 1/content.xml')); - } - - public function testChartBarHorizontal() - { - $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); - $oSeries->setShowSeriesName(true); - $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF4672A8')); - $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FFAB4744')); - $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF8AA64F')); - - $oBar = new Bar(); - $oBar->setBarDirection(Bar::DIRECTION_HORIZONTAL); - $oBar->addSeries($oSeries); - - $phpPresentation = new PhpPresentation(); - $oSlide = $phpPresentation->getActiveSlide(); - $oChart = $oSlide->createChartShape(); - $oChart->getPlotArea()->setType($oBar); - - $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); - - $element = '/office:document-content/office:body/office:chart/chart:chart'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('chart:bar', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); - - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'stylePlotArea\']/style:chart-properties'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:vertical', 'Object 1/content.xml')); - $this->assertFalse($pres->attributeElementExists($element, 'chart:three-dimensional', 'Object 1/content.xml')); - $this->assertFalse($pres->attributeElementExists($element, 'chart:right-angled-axes', 'Object 1/content.xml')); - } -} diff --git a/tests/PhpPresentation/Tests/Writer/ODPresentation/ChartsTest.php b/tests/PhpPresentation/Tests/Writer/ODPresentation/ChartsTest.php deleted file mode 100644 index 6d6d881d7..000000000 --- a/tests/PhpPresentation/Tests/Writer/ODPresentation/ChartsTest.php +++ /dev/null @@ -1,326 +0,0 @@ -getMockForAbstractClass('PhpOffice\PhpPresentation\Shape\Chart\Type\AbstractType'); - - $phpPresentation = new PhpPresentation(); - $oSlide = $phpPresentation->getActiveSlide(); - $oChart = $oSlide->createChartShape(); - $oChart->getPlotArea()->setType($oStub); - - TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); - } - - public function testTitleVisibility() - { - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oLine = new Line(); - $oShape->getPlotArea()->setType($oLine); - - $elementTitle = '/office:document-content/office:body/office:chart/chart:chart/chart:title'; - $elementStyle = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleTitle\']'; - - $this->assertTrue($oShape->getTitle()->isVisible()); - $this->assertInstanceOf('PhpOffice\PhpPresentation\Shape\Chart\Title', $oShape->getTitle()->setVisible(true)); - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); - $this->assertTrue($oXMLDoc->elementExists($elementTitle, 'Object 1/content.xml')); - $this->assertTrue($oXMLDoc->elementExists($elementStyle, 'Object 1/content.xml')); - - $this->assertInstanceOf('PhpOffice\PhpPresentation\Shape\Chart\Title', $oShape->getTitle()->setVisible(false)); - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); - $this->assertFalse($oXMLDoc->elementExists($elementTitle, 'Object 1/content.xml')); - $this->assertFalse($oXMLDoc->elementExists($elementStyle, 'Object 1/content.xml')); - } - - public function testChartBar3D() - { - $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); - $oSeries->setShowSeriesName(true); - $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF4672A8')); - $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FFAB4744')); - $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF8AA64F')); - - $oBar3D = new Bar3D(); - $oBar3D->addSeries($oSeries); - - $phpPresentation = new PhpPresentation(); - $oSlide = $phpPresentation->getActiveSlide(); - $oChart = $oSlide->createChartShape(); - $oChart->getPlotArea()->setType($oBar3D); - - $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); - - $element = '/office:document-content/office:body/office:chart/chart:chart'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('chart:bar', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); - - $element = '/office:document-content/office:body/office:chart/chart:chart/chart:plot-area/chart:series/chart:data-point'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'stylePlotArea\']/style:chart-properties'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('false', $pres->getElementAttribute($element, 'chart:vertical', 'Object 1/content.xml')); - $this->assertTrue($pres->attributeElementExists($element, 'chart:three-dimensional', 'Object 1/content.xml')); - $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:three-dimensional', 'Object 1/content.xml')); - $this->assertTrue($pres->attributeElementExists($element, 'chart:right-angled-axes', 'Object 1/content.xml')); - $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:right-angled-axes', 'Object 1/content.xml')); - } - - public function testChartBar3DHorizontal() - { - $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); - $oSeries->setShowSeriesName(true); - $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF4672A8')); - $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FFAB4744')); - $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF8AA64F')); - - $oBar3D = new Bar3D(); - $oBar3D->setBarDirection(Bar3D::DIRECTION_HORIZONTAL); - $oBar3D->addSeries($oSeries); - - $phpPresentation = new PhpPresentation(); - $oSlide = $phpPresentation->getActiveSlide(); - $oChart = $oSlide->createChartShape(); - $oChart->getPlotArea()->setType($oBar3D); - - $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); - - $element = '/office:document-content/office:body/office:chart/chart:chart'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('chart:bar', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); - - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'stylePlotArea\']/style:chart-properties'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:vertical', 'Object 1/content.xml')); - $this->assertTrue($pres->attributeElementExists($element, 'chart:three-dimensional', 'Object 1/content.xml')); - $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:three-dimensional', 'Object 1/content.xml')); - $this->assertTrue($pres->attributeElementExists($element, 'chart:right-angled-axes', 'Object 1/content.xml')); - $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:right-angled-axes', 'Object 1/content.xml')); - } - - public function testChartLine() - { - $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); - $oSeries->setShowSeriesName(true); - - $oLine = new Line(); - $oLine->addSeries($oSeries); - - $phpPresentation = new PhpPresentation(); - $oSlide = $phpPresentation->getActiveSlide(); - $oChart = $oSlide->createChartShape(); - $oChart->getPlotArea()->setType($oLine); - - $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); - - $element = '/office:document-content/office:body/office:chart/chart:chart'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('chart:line', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisX\']/style:chart-properties'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('false', $pres->getElementAttribute($element, 'chart:tick-marks-major-inner', 'Object 1/content.xml')); - $this->assertEquals('false', $pres->getElementAttribute($element, 'chart:tick-marks-major-outer', 'Object 1/content.xml')); - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisX\']/style:graphic-properties'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('0.026cm', $pres->getElementAttribute($element, 'svg:stroke-width', 'Object 1/content.xml')); - $this->assertEquals('#878787', $pres->getElementAttribute($element, 'svg:stroke-color', 'Object 1/content.xml')); - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisY\']/style:chart-properties'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('false', $pres->getElementAttribute($element, 'chart:tick-marks-major-inner', 'Object 1/content.xml')); - $this->assertEquals('false', $pres->getElementAttribute($element, 'chart:tick-marks-major-outer', 'Object 1/content.xml')); - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisY\']/style:graphic-properties'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('0.026cm', $pres->getElementAttribute($element, 'svg:stroke-width', 'Object 1/content.xml')); - $this->assertEquals('#878787', $pres->getElementAttribute($element, 'svg:stroke-color', 'Object 1/content.xml')); - } - - public function testChartPie() - { - $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); - $oSeries->setShowSeriesName(true); - $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF4672A8')); - $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FFAB4744')); - $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF8AA64F')); - - $oPie = new Pie(); - $oPie->addSeries($oSeries); - - $phpPresentation = new PhpPresentation(); - $oSlide = $phpPresentation->getActiveSlide(); - $oChart = $oSlide->createChartShape(); - $oChart->getPlotArea()->setType($oPie); - - $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); - - $element = '/office:document-content/office:body/office:chart/chart:chart'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('chart:circle', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); - - $element = '/office:document-content/office:body/office:chart/chart:chart/chart:plot-area/chart:series/chart:data-point'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisX\']/style:chart-properties'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:reverse-direction', 'Object 1/content.xml')); - - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisY\']/style:chart-properties'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:reverse-direction', 'Object 1/content.xml')); - } - - public function testChartPie3D() - { - $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); - $oSeries->setShowSeriesName(true); - $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF4672A8')); - $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FFAB4744')); - $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF8AA64F')); - - $oPie3D = new Pie3D(); - $oPie3D->addSeries($oSeries); - - $phpPresentation = new PhpPresentation(); - $oSlide = $phpPresentation->getActiveSlide(); - $oChart = $oSlide->createChartShape(); - $oChart->getPlotArea()->setType($oPie3D); - - $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); - - $element = '/office:document-content/office:body/office:chart/chart:chart'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('chart:circle', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); - - $element = '/office:document-content/office:body/office:chart/chart:chart/chart:plot-area/chart:series/chart:data-point'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisX\']/style:chart-properties'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:reverse-direction', 'Object 1/content.xml')); - - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisY\']/style:chart-properties'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:reverse-direction', 'Object 1/content.xml')); - } - - public function testChartPie3DExplosion() - { - $value = rand(0, 100); - - $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); - $oSeries->setShowSeriesName(true); - - $oPie3D = new Pie3D(); - $oPie3D->setExplosion($value); - $oPie3D->addSeries($oSeries); - - $phpPresentation = new PhpPresentation(); - $oSlide = $phpPresentation->getActiveSlide(); - $oChart = $oSlide->createChartShape(); - $oChart->getPlotArea()->setType($oPie3D); - - $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); - - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleSeries0\'][@style:family=\'chart\']/style:chart-properties'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals($value, $pres->getElementAttribute($element, 'chart:pie-offset', 'Object 1/content.xml')); - } - - public function testChartScatter() - { - $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); - $oSeries->setShowSeriesName(true); - - $oScatter = new Scatter(); - $oScatter->addSeries($oSeries); - - $phpPresentation = new PhpPresentation(); - $oSlide = $phpPresentation->getActiveSlide(); - $oChart = $oSlide->createChartShape(); - $oChart->getPlotArea()->setType($oScatter); - - $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); - - $element = '/office:document-content/office:body/office:chart/chart:chart'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $this->assertEquals('chart:scatter', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); - } - - public function testLegend() - { - $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); - $oSeries->setShowSeriesName(true); - - $oLine = new Line(); - $oLine->addSeries($oSeries); - - $phpPresentation = new PhpPresentation(); - $oSlide = $phpPresentation->getActiveSlide(); - $oChart = $oSlide->createChartShape(); - $oChart->getPlotArea()->setType($oLine); - - $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); - - $element = '/office:document-content/office:body/office:chart/chart:chart/chart:legend'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $element = '/office:document-content/office:body/office:chart/chart:chart/table:table/table:table-header-rows'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $element = '/office:document-content/office:body/office:chart/chart:chart/table:table/table:table-header-rows/table:table-row'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $element = '/office:document-content/office:body/office:chart/chart:chart/table:table/table:table-header-rows/table:table-row/table:table-cell'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - $element = '/office:document-content/office:body/office:chart/chart:chart/table:table/table:table-header-rows/table:table-row/table:table-cell[@office:value-type=\'string\']'; - $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); - } -} diff --git a/tests/PhpPresentation/Tests/Writer/ODPresentation/ContentTest.php b/tests/PhpPresentation/Tests/Writer/ODPresentation/ContentTest.php index ffd9d725e..248947d56 100644 --- a/tests/PhpPresentation/Tests/Writer/ODPresentation/ContentTest.php +++ b/tests/PhpPresentation/Tests/Writer/ODPresentation/ContentTest.php @@ -18,18 +18,20 @@ namespace PhpOffice\PhpPresentation\Tests\Writer\ODPresentation; use PhpOffice\Common\Drawing as CommonDrawing; +use PhpOffice\Common\Text; use PhpOffice\PhpPresentation\PhpPresentation; +use PhpOffice\PhpPresentation\Shape\Comment; +use PhpOffice\PhpPresentation\Shape\Media; use PhpOffice\PhpPresentation\Shape\RichText\Run; use PhpOffice\PhpPresentation\Slide\Transition; use PhpOffice\PhpPresentation\Style\Alignment; use PhpOffice\PhpPresentation\Style\Border; use PhpOffice\PhpPresentation\Style\Bullet; use PhpOffice\PhpPresentation\Style\Color; +use PhpOffice\PhpPresentation\Style\Fill; use PhpOffice\PhpPresentation\Writer\ODPresentation; use PhpOffice\PhpPresentation\Tests\TestHelperDOCX; use PhpOffice\Common\Drawing; -use PhpOffice\PhpPresentation\Style\Fill; -use PhpOffice\PhpPresentation\Style\PhpOffice\PhpPresentation\Style; /** * Test class for PhpOffice\PhpPresentation\Writer\ODPresentation\Manifest @@ -38,29 +40,110 @@ */ class ContentTest extends \PHPUnit_Framework_TestCase { + /** + * @var PhpPresentation + */ + protected $oPresentation; + + public function setUp() + { + parent::setUp(); + + $this->oPresentation = new PhpPresentation(); + } + /** * Executed before each method of the class */ public function tearDown() { + $this->oPresentation = null; TestHelperDOCX::clear(); } public function testDrawingWithHyperlink() { - $phpPresentation = new PhpPresentation(); - $oSlide = $phpPresentation->getActiveSlide(); + $oSlide = $this->oPresentation->getActiveSlide(); $oShape = $oSlide->createDrawingShape(); $oShape->setPath(PHPPRESENTATION_TESTS_BASE_DIR.'/resources/images/PhpPresentationLogo.png'); $oShape->getHyperlink()->setUrl('https://github.com/PHPOffice/PHPPresentation/'); - $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); + $pres = TestHelperDOCX::getDocument($this->oPresentation, 'ODPresentation'); $element = '/office:document-content/office:body/office:presentation/draw:page/draw:frame/office:event-listeners/presentation:event-listener'; $this->assertTrue($pres->elementExists($element, 'content.xml')); $this->assertEquals('https://github.com/PHPOffice/PHPPresentation/', $pres->getElementAttribute($element, 'xlink:href', 'content.xml')); } + public function testComment() + { + $expectedName = 'Name'; + $expectedText = 'Text'; + + $oAuthor = new Comment\Author(); + $oAuthor->setName($expectedName); + $oComment = new Comment(); + $oComment->setAuthor($oAuthor); + $oComment->setText($expectedText); + $this->oPresentation->getActiveSlide()->addShape($oComment); + + $pres = TestHelperDOCX::getDocument($this->oPresentation, 'ODPresentation'); + $element = '/office:document-content'; + $this->assertTrue($pres->elementExists($element, 'content.xml')); + $this->assertTrue($pres->attributeElementExists($element, 'xmlns:officeooo', 'content.xml')); + $element = '/office:document-content/office:body/office:presentation/draw:page/officeooo:annotation'; + $this->assertTrue($pres->elementExists($element, 'content.xml')); + $element = '/office:document-content/office:body/office:presentation/draw:page/officeooo:annotation/dc:creator'; + $this->assertTrue($pres->elementExists($element, 'content.xml')); + $this->assertEquals($expectedName, $pres->getElement($element, 'content.xml')->nodeValue); + $element = '/office:document-content/office:body/office:presentation/draw:page/officeooo:annotation/text:p'; + $this->assertTrue($pres->elementExists($element, 'content.xml')); + $this->assertEquals($expectedText, $pres->getElement($element, 'content.xml')->nodeValue); + } + + public function testCommentWithoutAuthor() + { + $oComment = new Comment(); + $this->oPresentation->getActiveSlide()->addShape($oComment); + + $pres = TestHelperDOCX::getDocument($this->oPresentation, 'ODPresentation'); + $element = '/office:document-content/office:body/office:presentation/draw:page/officeooo:annotation'; + $this->assertTrue($pres->elementExists($element, 'content.xml')); + $this->assertFalse($pres->attributeElementExists($element, 'dc:creator', 'content.xml')); + } + + public function testFillGradientLinearRichText() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oShape = $oSlide->createRichTextShape(); + $oShape->getFill()->setFillType(Fill::FILL_GRADIENT_LINEAR)->setStartColor(new Color('FFFF7700'))->setEndColor(new Color('FFFFFFFF')); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $element = '/office:document-styles/office:styles/draw:gradient'; + $this->assertEquals('gradient_'.$oShape->getFill()->getHashCode(), $pres->getElementAttribute($element, 'draw:name', 'styles.xml')); + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'gr1\']/style:graphic-properties'; + $this->assertTrue($pres->elementExists($element, 'content.xml')); + $this->assertEquals('gradient', $pres->getElementAttribute($element, 'draw:fill', 'content.xml')); + $this->assertEquals('gradient_'.$oShape->getFill()->getHashCode(), $pres->getElementAttribute($element, 'draw:fill-gradient-name', 'content.xml')); + } + + public function testFillSolidRichText() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oShape = $oSlide->createRichTextShape(); + $oShape->getFill()->setFillType(Fill::FILL_SOLID)->setRotation(90)->setStartColor(new Color('FF4672A8'))->setEndColor(new Color('FF4672A8')); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'gr1\']/style:graphic-properties'; + $this->assertTrue($pres->elementExists($element, 'content.xml')); + $this->assertEquals('solid', $pres->getElementAttribute($element, 'draw:fill', 'content.xml')); + $this->assertEquals('#'.$oShape->getFill()->getStartColor()->getRGB(), $pres->getElementAttribute($element, 'draw:fill-color', 'content.xml')); + $this->assertEquals('#'.$oShape->getFill()->getEndColor()->getRGB(), $pres->getElementAttribute($element, 'draw:fill-color', 'content.xml')); + } + public function testGroup() { $phpPresentation = new PhpPresentation(); @@ -176,6 +259,47 @@ public function testListWithRichText() $this->assertTrue($pres->elementExists($element, 'content.xml')); } + public function testMedia() + { + $expectedName = 'MyName'; + $expectedWidth = rand(1, 100); + $expectedHeight = rand(1, 100); + $expectedX = rand(1, 100); + $expectedY = rand(1, 100); + + $oMedia = new Media(); + $oMedia->setPath(PHPPRESENTATION_TESTS_BASE_DIR . '/resources/videos/sintel_trailer-480p.ogv') + ->setName($expectedName) + ->setResizeProportional(false) + ->setHeight($expectedHeight) + ->setWidth($expectedWidth) + ->setOffsetX($expectedX) + ->setOffsetY($expectedY); + + $expectedWidth = Text::numberFormat(CommonDrawing::pixelsToCentimeters($expectedWidth), 3) . 'cm'; + $expectedHeight = Text::numberFormat(CommonDrawing::pixelsToCentimeters($expectedHeight), 3) . 'cm'; + $expectedX = Text::numberFormat(CommonDrawing::pixelsToCentimeters($expectedX), 3) . 'cm'; + $expectedY = Text::numberFormat(CommonDrawing::pixelsToCentimeters($expectedY), 3) . 'cm'; + + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oSlide->addShape($oMedia); + + $xmlObject = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $element = '/office:document-content/office:body/office:presentation/draw:page/draw:frame'; + $this->assertTrue($xmlObject->elementExists($element, 'content.xml')); + $this->assertEquals($expectedName, $xmlObject->getElementAttribute($element, 'draw:name', 'content.xml')); + $this->assertEquals($expectedWidth, $xmlObject->getElementAttribute($element, 'svg:width', 'content.xml')); + $this->assertEquals($expectedHeight, $xmlObject->getElementAttribute($element, 'svg:height', 'content.xml')); + $this->assertEquals($expectedX, $xmlObject->getElementAttribute($element, 'svg:x', 'content.xml')); + $this->assertEquals($expectedY, $xmlObject->getElementAttribute($element, 'svg:y', 'content.xml')); + $element = '/office:document-content/office:body/office:presentation/draw:page/draw:frame/draw:plugin'; + $this->assertTrue($xmlObject->elementExists($element, 'content.xml')); + $this->assertEquals('application/vnd.sun.star.media', $xmlObject->getElementAttribute($element, 'draw:mime-type', 'content.xml')); + $this->assertStringStartsWith('Pictures/', $xmlObject->getElementAttribute($element, 'xlink:href', 'content.xml')); + $this->assertStringEndsWith('ogv', $xmlObject->getElementAttribute($element, 'xlink:href', 'content.xml')); + } + public function testNote() { $oPhpPresentation = new PhpPresentation(); @@ -222,6 +346,28 @@ public function testRichTextAutoShrink() $this->assertEquals('true', $pres->getElementAttribute($element, 'draw:auto-grow-width', 'content.xml')); } + public function testRichTextRunLanguage() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oRichText = $oSlide->createRichTextShape(); + $oRun = $oRichText->createTextRun('MyText'); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $expectedElement = '/office:document-content/office:automatic-styles/style:style[@style:name=\'T_'.$oRun->getHashCode().'\']/style:text-properties'; + $this->assertTrue($pres->elementExists($expectedElement, 'content.xml')); + $this->assertTrue($pres->attributeElementExists($expectedElement, 'fo:language', 'content.xml')); + $this->assertEquals('en-US', $pres->getElementAttribute($expectedElement, 'fo:language', 'content.xml')); + + $oRun->setLanguage('de_DE'); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $expectedElement = '/office:document-content/office:automatic-styles/style:style[@style:name=\'T_'.$oRun->getHashCode().'\']/style:text-properties'; + $this->assertTrue($pres->elementExists($expectedElement, 'content.xml')); + $this->assertTrue($pres->attributeElementExists($expectedElement, 'fo:language', 'content.xml')); + $this->assertEquals('de_DE', $pres->getElementAttribute($expectedElement, 'fo:language', 'content.xml')); + } + public function testRichTextBorder() { $phpPresentation = new PhpPresentation(); @@ -400,7 +546,7 @@ public function testStyleFont() $oRun = $oRichText->createTextRun('Run1'); $oRun->getFont()->setBold(true); - $expectedHashCode = $oRun->getFont()->getHashCode(); + $expectedHashCode = $oRun->getHashCode(); $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); @@ -702,8 +848,26 @@ public function testTransition() } } + $oTransition->setTimeTrigger(false); $oTransition->setManualTrigger(true); $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); $this->assertContains('manual', $pres->getElementAttribute($element, 'presentation:transition-type', 'content.xml')); } + + public function testVisibility() + { + $expectedElement = '/office:document-content/office:automatic-styles/style:style[@style:name=\'stylePage0\']/style:drawing-page-properties'; + + $pres = TestHelperDOCX::getDocument($this->oPresentation, 'ODPresentation'); + $this->assertTrue($pres->elementExists($expectedElement, 'content.xml')); + $this->assertFalse($pres->attributeElementExists($expectedElement, 'presentation:visibility', 'content.xml')); + + $oSlide = $this->oPresentation->getActiveSlide(); + $oSlide->setIsVisible(false); + + $pres = TestHelperDOCX::getDocument($this->oPresentation, 'ODPresentation'); + $this->assertTrue($pres->elementExists($expectedElement, 'content.xml')); + $this->assertTrue($pres->attributeElementExists($expectedElement, 'presentation:visibility', 'content.xml')); + $this->assertEquals('hidden', $pres->getElementAttribute($expectedElement, 'presentation:visibility', 'content.xml')); + } } diff --git a/tests/PhpPresentation/Tests/Writer/ODPresentation/DrawingTest.php b/tests/PhpPresentation/Tests/Writer/ODPresentation/DrawingTest.php deleted file mode 100644 index 004fa1b48..000000000 --- a/tests/PhpPresentation/Tests/Writer/ODPresentation/DrawingTest.php +++ /dev/null @@ -1,55 +0,0 @@ -getActiveSlide(); - $oGroup = $oSlide->createGroup(); - - $oDrawing = new Drawing(); - $this->assertInternalType('array', $oDrawing->allDrawings($oPhpPresentation)); - $this->assertEmpty($oDrawing->allDrawings($oPhpPresentation)); - - $oGroup->createDrawingShape(); - $oGroup->createDrawingShape(); - $oGroup->createDrawingShape(); - - $this->assertInternalType('array', $oDrawing->allDrawings($oPhpPresentation)); - $this->assertCount(3, $oDrawing->allDrawings($oPhpPresentation)); - } -} diff --git a/tests/PhpPresentation/Tests/Writer/ODPresentation/ManifestTest.php b/tests/PhpPresentation/Tests/Writer/ODPresentation/ManifestTest.php deleted file mode 100644 index 5fbe01fa7..000000000 --- a/tests/PhpPresentation/Tests/Writer/ODPresentation/ManifestTest.php +++ /dev/null @@ -1,70 +0,0 @@ -getActiveSlide(); - $oShape = $oSlide->createDrawingShape(); - $oShape->setPath(PHPPRESENTATION_TESTS_BASE_DIR.'/resources/images/PhpPresentationLogo.png'); - - $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); - $element = '/manifest:manifest/manifest:file-entry[5]'; - $this->assertTrue($pres->elementExists($element, 'META-INF/manifest.xml')); - $this->assertEquals('Pictures/'.md5($oShape->getPath()).'.'.$oShape->getExtension(), $pres->getElementAttribute($element, 'manifest:full-path', 'META-INF/manifest.xml')); - } - - public function testMemoryDrawing() - { - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = new MemoryDrawing(); - - $gdImage = @imagecreatetruecolor(140, 20); - $textColor = imagecolorallocate($gdImage, 255, 255, 255); - imagestring($gdImage, 1, 5, 5, 'Created with PhpPresentation', $textColor); - $oShape->setImageResource($gdImage)->setRenderingFunction(MemoryDrawing::RENDERING_JPEG)->setMimeType(MemoryDrawing::MIMETYPE_DEFAULT); - $oSlide->addShape($oShape); - - $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); - $element = '/manifest:manifest/manifest:file-entry[5]'; - $this->assertTrue($pres->elementExists($element, 'META-INF/manifest.xml')); - $this->assertEquals('Pictures/'.$oShape->getIndexedFilename(), $pres->getElementAttribute($element, 'manifest:full-path', 'META-INF/manifest.xml')); - } -} diff --git a/tests/PhpPresentation/Tests/Writer/ODPresentation/MetaInfManifestTest.php b/tests/PhpPresentation/Tests/Writer/ODPresentation/MetaInfManifestTest.php new file mode 100644 index 000000000..5e5e77620 --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/ODPresentation/MetaInfManifestTest.php @@ -0,0 +1,146 @@ +getActiveSlide(); + $oShape = $oSlide->createDrawingShape(); + $oShape->setPath(PHPPRESENTATION_TESTS_BASE_DIR.'/resources/images/PhpPresentationLogo.png'); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $element = '/manifest:manifest/manifest:file-entry[5]'; + $this->assertTrue($pres->elementExists($element, 'META-INF/manifest.xml')); + $this->assertStringStartsWith('Pictures/PhpPresentationLogo', $pres->getElementAttribute($element, 'manifest:full-path', 'META-INF/manifest.xml')); + $this->assertStringEndsWith('.png', $pres->getElementAttribute($element, 'manifest:full-path', 'META-INF/manifest.xml')); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage does not exist + */ + public function testDrawingException() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oShape = $oSlide->createDrawingShape(); + $oShape->setPath(PHPPRESENTATION_TESTS_BASE_DIR.'/resources/images/filedoesntexist.png', false); + + TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + } + + public function testMemoryDrawing() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oShape = new Drawing\Gd(); + + $gdImage = @imagecreatetruecolor(140, 20); + $textColor = imagecolorallocate($gdImage, 255, 255, 255); + imagestring($gdImage, 1, 5, 5, 'Created with PhpPresentation', $textColor); + $oShape->setImageResource($gdImage)->setRenderingFunction(Drawing\Gd::RENDERING_JPEG)->setMimeType(Drawing\Gd::MIMETYPE_DEFAULT); + $oSlide->addShape($oShape); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $element = '/manifest:manifest/manifest:file-entry[5]'; + $this->assertTrue($pres->elementExists($element, 'META-INF/manifest.xml')); + $this->assertEquals('Pictures/'.$oShape->getIndexedFilename(), $pres->getElementAttribute($element, 'manifest:full-path', 'META-INF/manifest.xml')); + } + + public function testDrawingZip() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + + $oShape = new Drawing\ZipFile(); + $oShape->setPath('zip://'.PHPPRESENTATION_TESTS_BASE_DIR.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'files'.DIRECTORY_SEPARATOR.'Sample_01_Simple.pptx#ppt/media/phppowerpoint_logo1.gif', false); + $oSlide->addShape($oShape); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $element = '/manifest:manifest/manifest:file-entry[5]'; + $this->assertTrue($pres->elementExists($element, 'META-INF/manifest.xml')); + $this->assertEquals('Pictures/'.$oShape->getIndexedFilename(), $pres->getElementAttribute($element, 'manifest:full-path', 'META-INF/manifest.xml')); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage does not exist + */ + public function testDrawingZipException() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + + $oShape = new Drawing\ZipFile(); + $oShape->setPath('zip://'.PHPPRESENTATION_TESTS_BASE_DIR.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'files'.DIRECTORY_SEPARATOR.'filedoesntexist.zip', false); + $oSlide->addShape($oShape); + + TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + } + + public function testDrawingBase64() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oShape = new Drawing\Base64(); + $oShape->setData('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIAtAFAAMBIgACEQEDEQH/xAAcAAEAAwEBAQEBAAAAAAAAAAAABAUGAwcCAQj/xABdEAABAwICAwYQCgcGBQIFBQEAAQIDBAUGERIhMQcTFkFR0RQVIjU2VFVhcXORkpOxssEXMjRSU3KBlKHSIzM3QlZ0giRioqPC4aSz0+LwQ2QlY2XD8SZFR4OERv/EABsBAQACAwEBAAAAAAAAAAAAAAADBQIEBgEH/8QAQREAAQMCBAMDCgUDAwQDAQEAAAECAwQREiExUQUTQRRhcSIyM1KBkaGxwfAVIzRC0TZy4QZT8RYkkqIlQ2KCNf/aAAwDAQACEQMRAD8A+eklo7l0f3dnMOklo7l0f3dnMTQddy2bIVeJ25C6SWjuXR/d2cw6SWjuXR/d2cxNA5bNkPMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7t5iaBy2bIMTtzhT0VJSaXQtLDBp5aW9xo3PLlyO4BkiImh5cAAyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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'); + $oSlide->addShape($oShape); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $element = '/manifest:manifest/manifest:file-entry[5]'; + $this->assertTrue($pres->elementExists($element, 'META-INF/manifest.xml')); + $this->assertEquals('Pictures/'.$oShape->getIndexedFilename(), $pres->getElementAttribute($element, 'manifest:full-path', 'META-INF/manifest.xml')); + } + + public function testSlideBackground() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oBkgImage = new Image(); + $oBkgImage->setPath(PHPPRESENTATION_TESTS_BASE_DIR.'/resources/images/PhpPresentationLogo.png'); + $oSlide->setBackground($oBkgImage); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $element = '/manifest:manifest/manifest:file-entry[5]'; + $this->assertTrue($pres->elementExists($element, 'META-INF/manifest.xml')); + $this->assertEquals('Pictures/'.str_replace(' ', '_', $oBkgImage->getIndexedFilename(0)), $pres->getElementAttribute($element, 'manifest:full-path', 'META-INF/manifest.xml')); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/ODPresentation/ObjectsChartTest.php b/tests/PhpPresentation/Tests/Writer/ODPresentation/ObjectsChartTest.php new file mode 100644 index 000000000..4bc7432e1 --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/ODPresentation/ObjectsChartTest.php @@ -0,0 +1,779 @@ +getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); + $oBar = new Bar(); + $oBar->addSeries($oSeries); + $oShape->getPlotArea()->setType($oBar); + $oShape->getPlotArea()->getAxisX()->getFont()->getColor()->setRGB('AABBCC'); + $oShape->getPlotArea()->getAxisX()->getFont()->setItalic(true); + + $oShape->getPlotArea()->getAxisY()->getFont()->getColor()->setRGB('00FF00'); + $oShape->getPlotArea()->getAxisY()->getFont()->setSize(16); + $oShape->getPlotArea()->getAxisY()->getFont()->setName('Arial'); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertTrue($oXMLDoc->fileExists('Object 1/content.xml')); + + $element = '/office:document-content/office:body/office:chart/chart:chart'; + $this->assertTrue($oXMLDoc->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('chart:bar', $oXMLDoc->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisX\']/style:text-properties'; + $this->assertTrue($oXMLDoc->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('#AABBCC', $oXMLDoc->getElementAttribute($element, 'fo:color', 'Object 1/content.xml'));//Color XAxis + $this->assertEquals('italic', $oXMLDoc->getElementAttribute($element, 'fo:font-style', 'Object 1/content.xml'));//Italic XAxis + $this->assertEquals('10pt', $oXMLDoc->getElementAttribute($element, 'fo:font-size', 'Object 1/content.xml'));//Size XAxis + $this->assertEquals('Calibri', $oXMLDoc->getElementAttribute($element, 'fo:font-family', 'Object 1/content.xml'));//Size XAxis + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisY\']/style:text-properties'; + $this->assertTrue($oXMLDoc->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('#00FF00', $oXMLDoc->getElementAttribute($element, 'fo:color', 'Object 1/content.xml'));//Color YAxis + $this->assertEquals('normal', $oXMLDoc->getElementAttribute($element, 'fo:font-style', 'Object 1/content.xml'));//Italic YAxis + $this->assertEquals('16pt', $oXMLDoc->getElementAttribute($element, 'fo:font-size', 'Object 1/content.xml'));//Size YAxis + $this->assertEquals('Arial', $oXMLDoc->getElementAttribute($element, 'fo:font-family', 'Object 1/content.xml'));//Size YAxis + } + + public function testLegend() + { + $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); + $oSeries->setShowSeriesName(true); + + $oLine = new Line(); + $oLine->addSeries($oSeries); + + $phpPresentation = new PhpPresentation(); + $oSlide = $phpPresentation->getActiveSlide(); + $oChart = $oSlide->createChartShape(); + $oChart->getPlotArea()->setType($oLine); + + $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); + + $element = '/office:document-content/office:body/office:chart/chart:chart/chart:legend'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $element = '/office:document-content/office:body/office:chart/chart:chart/table:table/table:table-header-rows'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $element = '/office:document-content/office:body/office:chart/chart:chart/table:table/table:table-header-rows/table:table-row'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $element = '/office:document-content/office:body/office:chart/chart:chart/table:table/table:table-header-rows/table:table-row/table:table-cell'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $element = '/office:document-content/office:body/office:chart/chart:chart/table:table/table:table-header-rows/table:table-row/table:table-cell[@office:value-type=\'string\']'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + } + + public function testTitleVisibility() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oLine = new Line(); + $oShape->getPlotArea()->setType($oLine); + + $elementTitle = '/office:document-content/office:body/office:chart/chart:chart/chart:title'; + $elementStyle = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleTitle\']'; + + $this->assertTrue($oShape->getTitle()->isVisible()); + $this->assertInstanceOf('PhpOffice\PhpPresentation\Shape\Chart\Title', $oShape->getTitle()->setVisible(true)); + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertTrue($oXMLDoc->elementExists($elementTitle, 'Object 1/content.xml')); + $this->assertTrue($oXMLDoc->elementExists($elementStyle, 'Object 1/content.xml')); + + $this->assertInstanceOf('PhpOffice\PhpPresentation\Shape\Chart\Title', $oShape->getTitle()->setVisible(false)); + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertFalse($oXMLDoc->elementExists($elementTitle, 'Object 1/content.xml')); + $this->assertFalse($oXMLDoc->elementExists($elementStyle, 'Object 1/content.xml')); + } + + public function testTypeArea() + { + $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); + $oSeries->getFill()->setStartColor(new Color('FF93A9CE')); + + $oArea = new Area(); + $oArea->addSeries($oSeries); + + $phpPresentation = new PhpPresentation(); + $oSlide = $phpPresentation->getActiveSlide(); + $oChart = $oSlide->createChartShape(); + $oChart->getPlotArea()->setType($oArea); + + $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); + + $element = '/office:document-content/office:body/office:chart/chart:chart'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('chart:area', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); + + $element = '/office:document-content/office:body/office:chart/chart:chart/chart:plot-area/chart:series'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('chart:area', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleSeries0\']/style:graphic-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertFalse($pres->attributeElementExists($element, 'draw:fill', 'Object 1/content.xml')); + $this->assertTrue($pres->attributeElementExists($element, 'draw:fill-color', 'Object 1/content.xml')); + $this->assertEquals('#93A9CE', $pres->getElementAttribute($element, 'draw:fill-color', 'Object 1/content.xml')); + } + + public function testTypeBar() + { + $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); + $oSeries->setShowSeriesName(true); + $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF4672A8')); + $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FFAB4744')); + $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF8AA64F')); + + $oBar = new Bar(); + $oBar->addSeries($oSeries); + + $phpPresentation = new PhpPresentation(); + $oSlide = $phpPresentation->getActiveSlide(); + $oChart = $oSlide->createChartShape(); + $oChart->getPlotArea()->setType($oBar); + + $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); + + $element = '/office:document-content/office:body/office:chart/chart:chart'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('chart:bar', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); + + $element = '/office:document-content/office:body/office:chart/chart:chart/chart:plot-area/chart:series/chart:data-point'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'stylePlotArea\']/style:chart-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('false', $pres->getElementAttribute($element, 'chart:vertical', 'Object 1/content.xml')); + $this->assertFalse($pres->attributeElementExists($element, 'chart:three-dimensional', 'Object 1/content.xml')); + $this->assertFalse($pres->attributeElementExists($element, 'chart:right-angled-axes', 'Object 1/content.xml')); + $this->assertEquals('false', $pres->getElementAttribute($element, 'chart:stacked', 'Object 1/content.xml')); + $this->assertEquals('0', $pres->getElementAttribute($element, 'chart:overlap', 'Object 1/content.xml')); + $this->assertFalse($pres->attributeElementExists($element, 'chart:percentage', 'Object 1/content.xml')); + $this->assertEquals('value', $pres->getElementAttribute($element, 'chart:data-label-number', 'Object 1/content.xml')); + } + + public function testTypeBarGroupingStacked() + { + $oBar = new Bar(); + $oBar->addSeries(new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2))); + $oBar->setBarGrouping(Bar::GROUPING_STACKED); + + $phpPresentation = new PhpPresentation(); + $oSlide = $phpPresentation->getActiveSlide(); + $oChart = $oSlide->createChartShape(); + $oChart->getPlotArea()->setType($oBar); + + $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'stylePlotArea\']/style:chart-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:stacked', 'Object 1/content.xml')); + $this->assertEquals('100', $pres->getElementAttribute($element, 'chart:overlap', 'Object 1/content.xml')); + $this->assertFalse($pres->attributeElementExists($element, 'chart:percentage', 'Object 1/content.xml')); + $this->assertEquals('value', $pres->getElementAttribute($element, 'chart:data-label-number', 'Object 1/content.xml')); + } + + public function testTypeBarGroupingPercentStacked() + { + $oBar = new Bar(); + $oBar->addSeries(new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2))); + $oBar->setBarGrouping(Bar::GROUPING_PERCENTSTACKED); + + $phpPresentation = new PhpPresentation(); + $oSlide = $phpPresentation->getActiveSlide(); + $oChart = $oSlide->createChartShape(); + $oChart->getPlotArea()->setType($oBar); + + $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'stylePlotArea\']/style:chart-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:stacked', 'Object 1/content.xml')); + $this->assertEquals('100', $pres->getElementAttribute($element, 'chart:overlap', 'Object 1/content.xml')); + $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:percentage', 'Object 1/content.xml')); + $this->assertEquals('percentage', $pres->getElementAttribute($element, 'chart:data-label-number', 'Object 1/content.xml')); + } + + public function testTypeBarHorizontal() + { + $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); + $oSeries->setShowSeriesName(true); + $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF4672A8')); + $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FFAB4744')); + $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF8AA64F')); + + $oBar = new Bar(); + $oBar->setBarDirection(Bar::DIRECTION_HORIZONTAL); + $oBar->addSeries($oSeries); + + $phpPresentation = new PhpPresentation(); + $oSlide = $phpPresentation->getActiveSlide(); + $oChart = $oSlide->createChartShape(); + $oChart->getPlotArea()->setType($oBar); + + $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); + + $element = '/office:document-content/office:body/office:chart/chart:chart'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('chart:bar', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'stylePlotArea\']/style:chart-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:vertical', 'Object 1/content.xml')); + $this->assertFalse($pres->attributeElementExists($element, 'chart:three-dimensional', 'Object 1/content.xml')); + $this->assertFalse($pres->attributeElementExists($element, 'chart:right-angled-axes', 'Object 1/content.xml')); + } + + public function testTypeBar3D() + { + $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); + $oSeries->setShowSeriesName(true); + $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF4672A8')); + $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FFAB4744')); + $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF8AA64F')); + + $oBar3D = new Bar3D(); + $oBar3D->addSeries($oSeries); + + $phpPresentation = new PhpPresentation(); + $oSlide = $phpPresentation->getActiveSlide(); + $oChart = $oSlide->createChartShape(); + $oChart->getPlotArea()->setType($oBar3D); + + $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); + + $element = '/office:document-content/office:body/office:chart/chart:chart'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('chart:bar', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); + + $element = '/office:document-content/office:body/office:chart/chart:chart/chart:plot-area/chart:series/chart:data-point'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'stylePlotArea\']/style:chart-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('false', $pres->getElementAttribute($element, 'chart:vertical', 'Object 1/content.xml')); + $this->assertTrue($pres->attributeElementExists($element, 'chart:three-dimensional', 'Object 1/content.xml')); + $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:three-dimensional', 'Object 1/content.xml')); + $this->assertTrue($pres->attributeElementExists($element, 'chart:right-angled-axes', 'Object 1/content.xml')); + $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:right-angled-axes', 'Object 1/content.xml')); + } + + public function testTypeBar3DHorizontal() + { + $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); + $oSeries->setShowSeriesName(true); + $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF4672A8')); + $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FFAB4744')); + $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF8AA64F')); + + $oBar3D = new Bar3D(); + $oBar3D->setBarDirection(Bar3D::DIRECTION_HORIZONTAL); + $oBar3D->addSeries($oSeries); + + $phpPresentation = new PhpPresentation(); + $oSlide = $phpPresentation->getActiveSlide(); + $oChart = $oSlide->createChartShape(); + $oChart->getPlotArea()->setType($oBar3D); + + $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); + + $element = '/office:document-content/office:body/office:chart/chart:chart'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('chart:bar', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'stylePlotArea\']/style:chart-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:vertical', 'Object 1/content.xml')); + $this->assertTrue($pres->attributeElementExists($element, 'chart:three-dimensional', 'Object 1/content.xml')); + $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:three-dimensional', 'Object 1/content.xml')); + $this->assertTrue($pres->attributeElementExists($element, 'chart:right-angled-axes', 'Object 1/content.xml')); + $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:right-angled-axes', 'Object 1/content.xml')); + } + + public function testTypeLine() + { + $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); + $oSeries->setShowSeriesName(true); + + $oLine = new Line(); + $oLine->addSeries($oSeries); + + $phpPresentation = new PhpPresentation(); + $oSlide = $phpPresentation->getActiveSlide(); + $oChart = $oSlide->createChartShape(); + $oChart->getPlotArea()->setType($oLine); + + $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); + + $element = '/office:document-content/office:body/office:chart/chart:chart'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('chart:line', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisX\']/style:chart-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('false', $pres->getElementAttribute($element, 'chart:tick-marks-major-inner', 'Object 1/content.xml')); + $this->assertEquals('false', $pres->getElementAttribute($element, 'chart:tick-marks-major-outer', 'Object 1/content.xml')); + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisX\']/style:graphic-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('0.026cm', $pres->getElementAttribute($element, 'svg:stroke-width', 'Object 1/content.xml')); + $this->assertEquals('#878787', $pres->getElementAttribute($element, 'svg:stroke-color', 'Object 1/content.xml')); + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisY\']/style:chart-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('false', $pres->getElementAttribute($element, 'chart:tick-marks-major-inner', 'Object 1/content.xml')); + $this->assertEquals('false', $pres->getElementAttribute($element, 'chart:tick-marks-major-outer', 'Object 1/content.xml')); + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisY\']/style:graphic-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('0.026cm', $pres->getElementAttribute($element, 'svg:stroke-width', 'Object 1/content.xml')); + $this->assertEquals('#878787', $pres->getElementAttribute($element, 'svg:stroke-color', 'Object 1/content.xml')); + } + + public function testTypeLineGridlines() + { + $arrayTests = array( + array( + 'dimension' => 'x', + 'styleName' => 'styleAxisXGridlinesMajor', + 'styleClass' => 'major', + 'methodAxis' => 'getAxisX', + 'methodGrid' => 'setMajorGridlines' + ), + array( + 'dimension' => 'x', + 'styleName' => 'styleAxisXGridlinesMinor', + 'styleClass' => 'minor', + 'methodAxis' => 'getAxisX', + 'methodGrid' => 'setMinorGridlines' + ), + array( + 'dimension' => 'y', + 'styleName' => 'styleAxisYGridlinesMajor', + 'styleClass' => 'major', + 'methodAxis' => 'getAxisY', + 'methodGrid' => 'setMajorGridlines' + ), + array( + 'dimension' => 'y', + 'styleName' => 'styleAxisYGridlinesMinor', + 'styleClass' => 'minor', + 'methodAxis' => 'getAxisY', + 'methodGrid' => 'setMinorGridlines' + ), + ); + $expectedColor = new Color(Color::COLOR_BLUE); + foreach ($arrayTests as $arrayTest) { + $expectedSizePts = rand(1, 100); + $expectedSizeCm = number_format(CommonDrawing::pointsToCentimeters($expectedSizePts), 2, '.', '').'cm'; + $expectedElementGrid = '/office:document-content/office:body/office:chart/chart:chart/chart:plot-area/chart:axis[@chart:dimension=\''.$arrayTest['dimension'].'\']/chart:grid'; + $expectedElementStyle = '/office:document-content/office:automatic-styles/style:style[@style:name=\''.$arrayTest['styleName'].'\']/style:graphic-properties'; + + $oPresentation = new PhpPresentation(); + $oSlide = $oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oLine = new Line(); + $oLine->addSeries(new Series('Downloads', array( + 'A' => 1, + 'B' => 2, + 'C' => 4, + 'D' => 3, + 'E' => 2, + ))); + $oShape->getPlotArea()->setType($oLine); + $oGridlines = new Gridlines(); + $oGridlines->getOutline()->getFill()->setFillType(Fill::FILL_SOLID)->setStartColor($expectedColor); + $oGridlines->getOutline()->setWidth($expectedSizePts); + $oShape->getPlotArea()->{$arrayTest['methodAxis']}()->{$arrayTest['methodGrid']}($oGridlines); + + $oXMLDoc = TestHelperDOCX::getDocument($oPresentation, 'ODPresentation'); + $this->assertTrue($oXMLDoc->fileExists('Object 1/content.xml')); + $this->assertTrue($oXMLDoc->elementExists($expectedElementGrid, 'Object 1/content.xml')); + $this->assertTrue($oXMLDoc->attributeElementExists($expectedElementGrid, 'chart:style-name', 'Object 1/content.xml')); + $this->assertEquals($arrayTest['styleName'], $oXMLDoc->getElementAttribute($expectedElementGrid, 'chart:style-name', 'Object 1/content.xml')); + $this->assertTrue($oXMLDoc->attributeElementExists($expectedElementGrid, 'chart:class', 'Object 1/content.xml')); + $this->assertEquals($arrayTest['styleClass'], $oXMLDoc->getElementAttribute($expectedElementGrid, 'chart:class', 'Object 1/content.xml')); + $this->assertTrue($oXMLDoc->elementExists($expectedElementStyle, 'Object 1/content.xml')); + $this->assertStringStartsWith($expectedSizeCm, $oXMLDoc->getElementAttribute($expectedElementStyle, 'svg:stroke-width', 'Object 1/content.xml')); + $this->assertStringEndsWith('cm', $oXMLDoc->getElementAttribute($expectedElementStyle, 'svg:stroke-width', 'Object 1/content.xml')); + $this->assertStringStartsWith('#', $oXMLDoc->getElementAttribute($expectedElementStyle, 'svg:stroke-color', 'Object 1/content.xml')); + $this->assertStringEndsWith($expectedColor->getRGB(), $oXMLDoc->getElementAttribute($expectedElementStyle, 'svg:stroke-color', 'Object 1/content.xml')); + + unset($oPresentation); + TestHelperDOCX::clear(); + } + } + + public function testTypeLineMarker() + { + $expectedSymbol1 = Marker::SYMBOL_PLUS; + $expectedSymbol2 = Marker::SYMBOL_DASH; + $expectedSymbol3 = Marker::SYMBOL_DOT; + $expectedSymbol4 = Marker::SYMBOL_TRIANGLE; + $expectedSymbol5 = Marker::SYMBOL_NONE; + + $expectedElement = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleSeries0\'][@style:family=\'chart\']/style:chart-properties'; + $expectedSize = rand(1, 100); + $expectedSizeCm = number_format(CommonDrawing::pointsToCentimeters($expectedSize), 2, '.', '').'cm'; + + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oLine = new Line(); + $oSeries = new Series('Downloads', array( + 'A' => 1, + 'B' => 2, + 'C' => 4, + 'D' => 3, + 'E' => 2, + )); + $oSeries->getMarker()->setSymbol($expectedSymbol1)->setSize($expectedSize); + $oLine->addSeries($oSeries); + $oShape->getPlotArea()->setType($oLine); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertTrue($oXMLDoc->fileExists('Object 1/content.xml')); + $this->assertTrue($oXMLDoc->elementExists($expectedElement, 'Object 1/content.xml')); + $this->assertEquals($expectedSymbol1, $oXMLDoc->getElementAttribute($expectedElement, 'chart:symbol-name', 'Object 1/content.xml')); + $this->assertEquals($expectedSizeCm, $oXMLDoc->getElementAttribute($expectedElement, 'chart:symbol-width', 'Object 1/content.xml')); + $this->assertEquals($expectedSizeCm, $oXMLDoc->getElementAttribute($expectedElement, 'chart:symbol-height', 'Object 1/content.xml')); + + $oSeries->getMarker()->setSymbol($expectedSymbol2); + $oLine->setSeries(array($oSeries)); + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertEquals('horizontal-bar', $oXMLDoc->getElementAttribute($expectedElement, 'chart:symbol-name', 'Object 1/content.xml')); + + $oSeries->getMarker()->setSymbol($expectedSymbol3); + $oLine->setSeries(array($oSeries)); + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertEquals('circle', $oXMLDoc->getElementAttribute($expectedElement, 'chart:symbol-name', 'Object 1/content.xml')); + + $oSeries->getMarker()->setSymbol($expectedSymbol4); + $oLine->setSeries(array($oSeries)); + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertEquals('arrow-up', $oXMLDoc->getElementAttribute($expectedElement, 'chart:symbol-name', 'Object 1/content.xml')); + + $oSeries->getMarker()->setSymbol($expectedSymbol5); + $oLine->setSeries(array($oSeries)); + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertFalse($oXMLDoc->attributeElementExists($expectedElement, 'chart:symbol-name', 'Object 1/content.xml')); + $this->assertFalse($oXMLDoc->attributeElementExists($expectedElement, 'chart:symbol-width', 'Object 1/content.xml')); + $this->assertFalse($oXMLDoc->attributeElementExists($expectedElement, 'chart:symbol-height', 'Object 1/content.xml')); + } + + public function testTypeLineSeriesOutline() + { + $expectedWidth = rand(1, 100); + $expectedWidthCm = number_format(CommonDrawing::pointsToCentimeters($expectedWidth), 3, '.', '').'cm'; + + $expectedElement = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleSeries0\'][@style:family=\'chart\']/style:graphic-properties'; + + $oColor = new Color(Color::COLOR_YELLOW); + $oOutline = new Outline(); + // Define the color + $oOutline->getFill()->setFillType(Fill::FILL_SOLID); + $oOutline->getFill()->setStartColor($oColor); + // Define the width (in points) + $oOutline->setWidth($expectedWidth); + + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oLine = new Line(); + $oSeries = new Series('Downloads', array( + 'A' => 1, + 'B' => 2, + 'C' => 4, + 'D' => 3, + 'E' => 2, + )); + $oLine->addSeries($oSeries); + $oShape->getPlotArea()->setType($oLine); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertTrue($oXMLDoc->fileExists('Object 1/content.xml')); + $this->assertTrue($oXMLDoc->elementExists($expectedElement, 'Object 1/content.xml')); + $this->assertTrue($oXMLDoc->attributeElementExists($expectedElement, 'svg:stroke-width', 'Object 1/content.xml')); + $this->assertEquals('0.079cm', $oXMLDoc->getElementAttribute($expectedElement, 'svg:stroke-width', 'Object 1/content.xml')); + $this->assertTrue($oXMLDoc->attributeElementExists($expectedElement, 'svg:stroke-color', 'Object 1/content.xml')); + $this->assertEquals('#4a7ebb', $oXMLDoc->getElementAttribute($expectedElement, 'svg:stroke-color', 'Object 1/content.xml')); + + $oSeries->setOutline($oOutline); + $oLine->setSeries(array($oSeries)); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertTrue($oXMLDoc->fileExists('Object 1/content.xml')); + $this->assertTrue($oXMLDoc->elementExists($expectedElement, 'Object 1/content.xml')); + $this->assertTrue($oXMLDoc->attributeElementExists($expectedElement, 'svg:stroke-width', 'Object 1/content.xml')); + $this->assertEquals($expectedWidthCm, $oXMLDoc->getElementAttribute($expectedElement, 'svg:stroke-width', 'Object 1/content.xml')); + $this->assertTrue($oXMLDoc->attributeElementExists($expectedElement, 'svg:stroke-color', 'Object 1/content.xml')); + $this->assertEquals('#'.$oColor->getRGB(), $oXMLDoc->getElementAttribute($expectedElement, 'svg:stroke-color', 'Object 1/content.xml')); + } + + public function testTypePie() + { + $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); + $oSeries->setShowSeriesName(true); + $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF4672A8')); + $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FFAB4744')); + $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF8AA64F')); + + $oPie = new Pie(); + $oPie->addSeries($oSeries); + + $phpPresentation = new PhpPresentation(); + $oSlide = $phpPresentation->getActiveSlide(); + $oChart = $oSlide->createChartShape(); + $oChart->getPlotArea()->setType($oPie); + + $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); + + $element = '/office:document-content/office:body/office:chart/chart:chart'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('chart:circle', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); + + $element = '/office:document-content/office:body/office:chart/chart:chart/chart:plot-area/chart:series/chart:data-point'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisX\']/style:chart-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:reverse-direction', 'Object 1/content.xml')); + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisY\']/style:chart-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:reverse-direction', 'Object 1/content.xml')); + } + + public function testTypePie3D() + { + $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); + $oSeries->setShowSeriesName(true); + $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF4672A8')); + $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FFAB4744')); + $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF8AA64F')); + + $oPie3D = new Pie3D(); + $oPie3D->addSeries($oSeries); + + $phpPresentation = new PhpPresentation(); + $oSlide = $phpPresentation->getActiveSlide(); + $oChart = $oSlide->createChartShape(); + $oChart->getPlotArea()->setType($oPie3D); + + $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); + + $element = '/office:document-content/office:body/office:chart/chart:chart'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('chart:circle', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); + + $element = '/office:document-content/office:body/office:chart/chart:chart/chart:plot-area/chart:series/chart:data-point'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisX\']/style:chart-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:reverse-direction', 'Object 1/content.xml')); + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleAxisY\']/style:chart-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('true', $pres->getElementAttribute($element, 'chart:reverse-direction', 'Object 1/content.xml')); + } + + public function testTypePie3DExplosion() + { + $value = rand(0, 100); + + $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); + $oSeries->setShowSeriesName(true); + + $oPie3D = new Pie3D(); + $oPie3D->setExplosion($value); + $oPie3D->addSeries($oSeries); + + $phpPresentation = new PhpPresentation(); + $oSlide = $phpPresentation->getActiveSlide(); + $oChart = $oSlide->createChartShape(); + $oChart->getPlotArea()->setType($oPie3D); + + $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); + + $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleSeries0\'][@style:family=\'chart\']/style:chart-properties'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals($value, $pres->getElementAttribute($element, 'chart:pie-offset', 'Object 1/content.xml')); + } + + public function testTypeScatter() + { + $oSeries = new Series('Series', array('Jan' => 1, 'Feb' => 5, 'Mar' => 2)); + $oSeries->setShowSeriesName(true); + + $oScatter = new Scatter(); + $oScatter->addSeries($oSeries); + + $phpPresentation = new PhpPresentation(); + $oSlide = $phpPresentation->getActiveSlide(); + $oChart = $oSlide->createChartShape(); + $oChart->getPlotArea()->setType($oScatter); + + $pres = TestHelperDOCX::getDocument($phpPresentation, 'ODPresentation'); + + $element = '/office:document-content/office:body/office:chart/chart:chart'; + $this->assertTrue($pres->elementExists($element, 'Object 1/content.xml')); + $this->assertEquals('chart:scatter', $pres->getElementAttribute($element, 'chart:class', 'Object 1/content.xml')); + } + + public function testTypeScatterMarker() + { + $expectedSymbol1 = Marker::SYMBOL_PLUS; + $expectedSymbol2 = Marker::SYMBOL_DASH; + $expectedSymbol3 = Marker::SYMBOL_DOT; + $expectedSymbol4 = Marker::SYMBOL_TRIANGLE; + $expectedSymbol5 = Marker::SYMBOL_NONE; + + $expectedElement = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleSeries0\'][@style:family=\'chart\']/style:chart-properties'; + $expectedSize = rand(1, 100); + $expectedSizeCm = number_format(CommonDrawing::pointsToCentimeters($expectedSize), 2, '.', '').'cm'; + + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oScatter = new Scatter(); + $oSeries = new Series('Downloads', array( + 'A' => 1, + 'B' => 2, + 'C' => 4, + 'D' => 3, + 'E' => 2, + )); + $oSeries->getMarker()->setSymbol($expectedSymbol1)->setSize($expectedSize); + $oScatter->addSeries($oSeries); + $oShape->getPlotArea()->setType($oScatter); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertTrue($oXMLDoc->fileExists('Object 1/content.xml')); + $this->assertTrue($oXMLDoc->elementExists($expectedElement, 'Object 1/content.xml')); + $this->assertEquals($expectedSymbol1, $oXMLDoc->getElementAttribute($expectedElement, 'chart:symbol-name', 'Object 1/content.xml')); + $this->assertEquals($expectedSizeCm, $oXMLDoc->getElementAttribute($expectedElement, 'chart:symbol-width', 'Object 1/content.xml')); + $this->assertEquals($expectedSizeCm, $oXMLDoc->getElementAttribute($expectedElement, 'chart:symbol-height', 'Object 1/content.xml')); + + $oSeries->getMarker()->setSymbol($expectedSymbol2); + $oScatter->setSeries(array($oSeries)); + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertEquals('horizontal-bar', $oXMLDoc->getElementAttribute($expectedElement, 'chart:symbol-name', 'Object 1/content.xml')); + + $oSeries->getMarker()->setSymbol($expectedSymbol3); + $oScatter->setSeries(array($oSeries)); + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertEquals('circle', $oXMLDoc->getElementAttribute($expectedElement, 'chart:symbol-name', 'Object 1/content.xml')); + + $oSeries->getMarker()->setSymbol($expectedSymbol4); + $oScatter->setSeries(array($oSeries)); + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertEquals('arrow-up', $oXMLDoc->getElementAttribute($expectedElement, 'chart:symbol-name', 'Object 1/content.xml')); + + $oSeries->getMarker()->setSymbol($expectedSymbol5); + $oScatter->setSeries(array($oSeries)); + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertFalse($oXMLDoc->attributeElementExists($expectedElement, 'chart:symbol-name', 'Object 1/content.xml')); + $this->assertFalse($oXMLDoc->attributeElementExists($expectedElement, 'chart:symbol-width', 'Object 1/content.xml')); + $this->assertFalse($oXMLDoc->attributeElementExists($expectedElement, 'chart:symbol-height', 'Object 1/content.xml')); + } + + public function testTypeScatterSeriesOutline() + { + $expectedWidth = rand(1, 100); + $expectedWidthCm = number_format(CommonDrawing::pointsToCentimeters($expectedWidth), 3, '.', '').'cm'; + + $expectedElement = '/office:document-content/office:automatic-styles/style:style[@style:name=\'styleSeries0\'][@style:family=\'chart\']/style:graphic-properties'; + + $oColor = new Color(Color::COLOR_YELLOW); + $oOutline = new Outline(); + // Define the color + $oOutline->getFill()->setFillType(Fill::FILL_SOLID); + $oOutline->getFill()->setStartColor($oColor); + // Define the width (in points) + $oOutline->setWidth($expectedWidth); + + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oScatter = new Scatter(); + $oSeries = new Series('Downloads', array( + 'A' => 1, + 'B' => 2, + 'C' => 4, + 'D' => 3, + 'E' => 2, + )); + $oScatter->addSeries($oSeries); + $oShape->getPlotArea()->setType($oScatter); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertTrue($oXMLDoc->fileExists('Object 1/content.xml')); + $this->assertTrue($oXMLDoc->elementExists($expectedElement, 'Object 1/content.xml')); + $this->assertTrue($oXMLDoc->attributeElementExists($expectedElement, 'svg:stroke-width', 'Object 1/content.xml')); + $this->assertEquals('0.079cm', $oXMLDoc->getElementAttribute($expectedElement, 'svg:stroke-width', 'Object 1/content.xml')); + $this->assertTrue($oXMLDoc->attributeElementExists($expectedElement, 'svg:stroke-color', 'Object 1/content.xml')); + $this->assertEquals('#4a7ebb', $oXMLDoc->getElementAttribute($expectedElement, 'svg:stroke-color', 'Object 1/content.xml')); + + $oSeries->setOutline($oOutline); + $oScatter->setSeries(array($oSeries)); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertTrue($oXMLDoc->fileExists('Object 1/content.xml')); + $this->assertTrue($oXMLDoc->elementExists($expectedElement, 'Object 1/content.xml')); + $this->assertTrue($oXMLDoc->attributeElementExists($expectedElement, 'svg:stroke-width', 'Object 1/content.xml')); + $this->assertEquals($expectedWidthCm, $oXMLDoc->getElementAttribute($expectedElement, 'svg:stroke-width', 'Object 1/content.xml')); + $this->assertTrue($oXMLDoc->attributeElementExists($expectedElement, 'svg:stroke-color', 'Object 1/content.xml')); + $this->assertEquals('#'.$oColor->getRGB(), $oXMLDoc->getElementAttribute($expectedElement, 'svg:stroke-color', 'Object 1/content.xml')); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/ODPresentation/StylesTest.php b/tests/PhpPresentation/Tests/Writer/ODPresentation/StylesTest.php index a741d2c1d..1fa7e8a1b 100644 --- a/tests/PhpPresentation/Tests/Writer/ODPresentation/StylesTest.php +++ b/tests/PhpPresentation/Tests/Writer/ODPresentation/StylesTest.php @@ -77,38 +77,6 @@ public function testCustomDocumentLayout() $this->assertEquals('sPL0', $pres->getElementAttribute($element, 'style:page-layout-name', 'styles.xml')); } - public function testFillGradientLinearRichText() - { - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createRichTextShape(); - $oShape->getFill()->setFillType(Fill::FILL_GRADIENT_LINEAR)->setStartColor(new Color('FFFF7700'))->setEndColor(new Color('FFFFFFFF')); - - $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); - $element = '/office:document-styles/office:styles/draw:gradient'; - $this->assertEquals('gradient_'.$oShape->getFill()->getHashCode(), $pres->getElementAttribute($element, 'draw:name', 'styles.xml')); - - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'gr1\']/style:graphic-properties'; - $this->assertTrue($pres->elementExists($element, 'content.xml')); - $this->assertEquals('gradient', $pres->getElementAttribute($element, 'draw:fill', 'content.xml')); - $this->assertEquals('gradient_'.$oShape->getFill()->getHashCode(), $pres->getElementAttribute($element, 'draw:fill-gradient-name', 'content.xml')); - } - - public function testFillSolidRichText() - { - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createRichTextShape(); - $oShape->getFill()->setFillType(Fill::FILL_SOLID)->setRotation(90)->setStartColor(new Color('FF4672A8'))->setEndColor(new Color('FF4672A8')); - - $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); - $element = '/office:document-content/office:automatic-styles/style:style[@style:name=\'gr1\']/style:graphic-properties'; - $this->assertTrue($pres->elementExists($element, 'content.xml')); - $this->assertEquals('solid', $pres->getElementAttribute($element, 'draw:fill', 'content.xml')); - $this->assertEquals('#'.$oShape->getFill()->getStartColor()->getRGB(), $pres->getElementAttribute($element, 'draw:fill-color', 'content.xml')); - $this->assertEquals('#'.$oShape->getFill()->getEndColor()->getRGB(), $pres->getElementAttribute($element, 'draw:fill-color', 'content.xml')); - } - public function testGradientTable() { $oPhpPresentation = new PhpPresentation(); diff --git a/tests/PhpPresentation/Tests/Writer/ODPresentationTest.php b/tests/PhpPresentation/Tests/Writer/ODPresentationTest.php index 72047913e..7ffc812b5 100644 --- a/tests/PhpPresentation/Tests/Writer/ODPresentationTest.php +++ b/tests/PhpPresentation/Tests/Writer/ODPresentationTest.php @@ -19,6 +19,7 @@ use PhpOffice\PhpPresentation\PhpPresentation; use PhpOffice\PhpPresentation\Writer\ODPresentation; +use PhpOffice\PhpPresentation\Tests\TestHelperDOCX; /** * Test class for PhpOffice\PhpPresentation\Writer\ODPresentation @@ -27,30 +28,24 @@ */ class ODPresentationTest extends \PHPUnit_Framework_TestCase { + public function tearDown() + { + parent::tearDown(); + + TestHelperDOCX::clear(); + } + /** * Test create new instance */ public function testConstruct() { - $objectPrefix = 'PhpOffice\\PhpPresentation\\Writer\\ODPresentation\\'; - $parts = array( - 'content' => 'Content', - 'manifest' => 'Manifest', - 'meta' => 'Meta', - 'mimetype' => 'Mimetype', - 'styles' => 'Styles', - 'drawing' => 'Drawing', - ); - $oPhpPresentation = new PhpPresentation(); $oPhpPresentation->getActiveSlide()->createDrawingShape(); $object = new ODPresentation($oPhpPresentation); $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $object->getPhpPresentation()); $this->assertEquals('./', $object->getDiskCachingDirectory()); - foreach ($parts as $partName => $objectName) { - $this->assertInstanceOf($objectPrefix . $objectName, $object->getWriterPart($partName)); - } $this->assertInstanceOf('PhpOffice\\PhpPresentation\\HashTable', $object->getDrawingHashTable()); } @@ -90,16 +85,6 @@ public function testSaveEmpty() $object->save(''); } - /** - * Test get writer part null - */ - public function testGetWriterPartNull() - { - $object = new ODPresentation(new PhpPresentation()); - - $this->assertNull($object->getWriterPart('foo')); - } - /** * Test get PhpPresentation exception * @@ -135,4 +120,24 @@ public function testSetUseDiskCachingException() $object = new ODPresentation(new PhpPresentation()); $object->setUseDiskCaching(true, 'foo'); } + + public function testFeatureThumbnail() + { + $imagePath = PHPPRESENTATION_TESTS_BASE_DIR.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.'PhpPresentationLogo.png'; + + $xPathManifest = '/manifest:manifest/manifest:file-entry[@manifest:media-type=\'image/png\'][@manifest:full-path=\'Thumbnails/thumbnail.png\']'; + + $oPhpPresentation = new PhpPresentation(); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertFalse($oXMLDoc->fileExists('Thumbnails/thumbnail.png')); + $this->assertTrue($oXMLDoc->fileExists('META-INF/manifest.xml')); + $this->assertFalse($oXMLDoc->elementExists($xPathManifest, 'META-INF/manifest.xml')); + + $oPhpPresentation->getPresentationProperties()->setThumbnailPath($imagePath); + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'ODPresentation'); + $this->assertTrue($oXMLDoc->fileExists('Thumbnails/thumbnail.png')); + $this->assertTrue($oXMLDoc->fileExists('META-INF/manifest.xml')); + $this->assertTrue($oXMLDoc->elementExists($xPathManifest, 'META-INF/manifest.xml')); + } } diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/AbstractPartTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/AbstractPartTest.php deleted file mode 100644 index 8003cf090..000000000 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/AbstractPartTest.php +++ /dev/null @@ -1,77 +0,0 @@ -setAccessible(true); - return $method->invokeArgs($obj, $args); - } - - /** - * Executed before each method of the class - */ - public function tearDown() - { - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage No parent \PhpOffice\PhpPresentation\Writer\WriterInterface assigned. - */ - public function testParentException() - { - $oDrawing = new Drawing(); - $oDrawing->getParentWriter(); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage The $parentWriter is not an instance of \PhpOffice\PhpPresentation\Writer\PowerPoint2007 - */ - public function testWriterException() - { - $oManifest = new Rels(); - $oManifest->setParentWriter(new ODPresentation()); - $oManifest->writeRelationships(); - } - - public function testXMLWriterWithDiskCaching() - { - $oPowerPoint2007 = new PowerPoint2007(); - $oPowerPoint2007->setUseDiskCaching(true); - $oRels = new Rels(); - $oRels->setParentWriter($oPowerPoint2007); - - $this->assertNotEmpty(\PHPUnit_Framework_Assert::readAttribute($this->runProtectedMethod($oRels, 'getXMLWriter'), 'tempFileName')); - } -} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/ChartAreaTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/ChartAreaTest.php deleted file mode 100644 index a6d1054af..000000000 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/ChartAreaTest.php +++ /dev/null @@ -1,73 +0,0 @@ - 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oArea = new Area(); - $oSeries = new Series('Downloads', $seriesData); - $oSeries->getFill()->setStartColor(new Color('FFAABBCC')); - $oArea->addSeries($oSeries); - $oShape->getPlotArea()->setType($oArea); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); - $element = '/c:chartSpace/c:chart/c:plotArea/c:areaChart'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:areaChart/c:ser'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - } -} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/ChartBarTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/ChartBarTest.php deleted file mode 100644 index af195a5b2..000000000 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/ChartBarTest.php +++ /dev/null @@ -1,81 +0,0 @@ - 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oBar = new Bar(); - $oSeries = new Series('Downloads', $seriesData); - $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_BLUE)); - $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKBLUE)); - $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKGREEN)); - $oSeries->getDataPointFill(3)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKRED)); - $oSeries->getDataPointFill(4)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKYELLOW)); - $oBar->addSeries($oSeries); - $oShape->getPlotArea()->setType($oBar); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); - $element = '/c:chartSpace/c:chart/c:plotArea/c:barChart'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser/c:dPt/c:spPr'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser/c:tx/c:v'; - $this->assertEquals($oSeries->getTitle(), $oXMLDoc->getElement($element, 'ppt/charts/'.$oShape->getIndexedFilename())->nodeValue); - } -} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/ChartTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/ChartTest.php deleted file mode 100644 index 4aa9a6218..000000000 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/ChartTest.php +++ /dev/null @@ -1,541 +0,0 @@ -writeChart(); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage The chart type provided could not be rendered. - */ - public function testPlotAreaBadType() - { - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $stub = $this->getMockForAbstractClass('PhpOffice\PhpPresentation\Shape\Chart\Type\AbstractType'); - $oShape->getPlotArea()->setType($stub); - - TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - } - - public function testTitleVisibility() - { - $element = '/c:chartSpace/c:chart/c:autoTitleDeleted'; - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oLine = new Line(); - $oShape->getPlotArea()->setType($oLine); - - $this->assertTrue($oShape->getTitle()->isVisible()); - $this->assertInstanceOf('PhpOffice\PhpPresentation\Shape\Chart\Title', $oShape->getTitle()->setVisible(true)); - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $this->assertEquals('0', $oXMLDoc->getElementAttribute($element, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); - - $this->assertInstanceOf('PhpOffice\PhpPresentation\Shape\Chart\Title', $oShape->getTitle()->setVisible(false)); - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $this->assertEquals('1', $oXMLDoc->getElementAttribute($element, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); - } - - public function testTypeBar3D() - { - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oBar3D = new Bar3D(); - $oSeries = new Series('Downloads', $seriesData); - $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_BLUE)); - $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKBLUE)); - $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKGREEN)); - $oSeries->getDataPointFill(3)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKRED)); - $oSeries->getDataPointFill(4)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKYELLOW)); - $oBar3D->addSeries($oSeries); - $oShape->getPlotArea()->setType($oBar3D); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); - $element = '/c:chartSpace/c:chart/c:plotArea/c:bar3DChart'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:ser'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:ser/c:dPt/c:spPr'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:ser/c:tx/c:v'; - $this->assertEquals($oSeries->getTitle(), $oXMLDoc->getElement($element, 'ppt/charts/'.$oShape->getIndexedFilename())->nodeValue); - } - - public function testTypeBar3DSubScript() - { - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oBar3D = new Bar3D(); - $oSeries = new Series('Downloads', $seriesData); - $oSeries->getFont()->setSubScript(true); - $oBar3D->addSeries($oSeries); - $oShape->getPlotArea()->setType($oBar3D); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $this->assertEquals('-25000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); - } - - public function testTypeBar3DSuperScript() - { - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oBar3D = new Bar3D(); - $oSeries = new Series('Downloads', $seriesData); - $oSeries->getFont()->setSuperScript(true); - $oBar3D->addSeries($oSeries); - $oShape->getPlotArea()->setType($oBar3D); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $this->assertEquals('30000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); - } - - public function testTypeBar3DBarDirection() - { - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oBar3D = new Bar3D(); - $oBar3D->setBarDirection(Bar3D::DIRECTION_HORIZONTAL); - $oSeries = new Series('Downloads', $seriesData); - $oBar3D->addSeries($oSeries); - $oShape->getPlotArea()->setType($oBar3D); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:barDir'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $this->assertEquals(Bar3D::DIRECTION_HORIZONTAL, $oXMLDoc->getElementAttribute($element, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); - } - - public function testTypeLine() - { - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oLine = new Line(); - $oSeries = new Series('Downloads', $seriesData); - $oLine->addSeries($oSeries); - $oShape->getPlotArea()->setType($oLine); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); - $element = '/c:chartSpace/c:chart/c:plotArea/c:lineChart'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:lineChart/c:ser'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:lineChart/c:ser/c:tx/c:v'; - $this->assertEquals($oSeries->getTitle(), $oXMLDoc->getElement($element, 'ppt/charts/'.$oShape->getIndexedFilename())->nodeValue); - } - - public function testTypeLineSubScript() - { - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oLine = new Line(); - $oSeries = new Series('Downloads', $seriesData); - $oSeries->getFont()->setSubScript(true); - $oLine->addSeries($oSeries); - $oShape->getPlotArea()->setType($oLine); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/c:chartSpace/c:chart/c:plotArea/c:lineChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $this->assertEquals('-25000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); - } - - public function testTypeLineSuperScript() - { - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oLine = new Line(); - $oSeries = new Series('Downloads', $seriesData); - $oSeries->getFont()->setSuperScript(true); - $oLine->addSeries($oSeries); - $oShape->getPlotArea()->setType($oLine); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/c:chartSpace/c:chart/c:plotArea/c:lineChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $this->assertEquals('30000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); - } - - public function testTypePie() - { - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oPie = new Pie(); - $oSeries = new Series('Downloads', $seriesData); - $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_BLUE)); - $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKBLUE)); - $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKGREEN)); - $oSeries->getDataPointFill(3)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKRED)); - $oSeries->getDataPointFill(4)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKYELLOW)); - $oPie->addSeries($oSeries); - $oShape->getPlotArea()->setType($oPie); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); - $element = '/c:chartSpace/c:chart/c:plotArea/c:pieChart'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:pieChart/c:ser'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:pieChart/c:ser/c:dPt/c:spPr'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:pieChart/c:ser/c:tx/c:v'; - $this->assertEquals($oSeries->getTitle(), $oXMLDoc->getElement($element, 'ppt/charts/'.$oShape->getIndexedFilename())->nodeValue); - } - public function testTypePie3D() - { - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oPie3D = new Pie3D(); - $oSeries = new Series('Downloads', $seriesData); - $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_BLUE)); - $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKBLUE)); - $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKGREEN)); - $oSeries->getDataPointFill(3)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKRED)); - $oSeries->getDataPointFill(4)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKYELLOW)); - $oPie3D->addSeries($oSeries); - $oShape->getPlotArea()->setType($oPie3D); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); - $element = '/c:chartSpace/c:chart/c:plotArea/c:pie3DChart'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:pie3DChart/c:ser'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:pie3DChart/c:ser/c:dPt/c:spPr'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:pie3DChart/c:ser/c:tx/c:v'; - $this->assertEquals($oSeries->getTitle(), $oXMLDoc->getElement($element, 'ppt/charts/'.$oShape->getIndexedFilename())->nodeValue); - } - - public function testTypePie3DExplosion() - { - $value = rand(1, 100); - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oPie3D = new Pie3D(); - $oPie3D->setExplosion($value); - $oSeries = new Series('Downloads', $seriesData); - $oPie3D->addSeries($oSeries); - $oShape->getPlotArea()->setType($oPie3D); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/c:chartSpace/c:chart/c:plotArea/c:pie3DChart/c:ser/c:explosion'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $this->assertEquals($value, $oXMLDoc->getElementAttribute($element, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); - } - - public function testTypePie3DSubScript() - { - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oPie3D = new Pie3D(); - $oSeries = new Series('Downloads', $seriesData); - $oSeries->getFont()->setSubScript(true); - $oPie3D->addSeries($oSeries); - $oShape->getPlotArea()->setType($oPie3D); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/c:chartSpace/c:chart/c:plotArea/c:pie3DChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $this->assertEquals('-25000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); - } - - public function testTypePie3DSuperScript() - { - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oPie3D = new Pie3D(); - $oSeries = new Series('Downloads', $seriesData); - $oSeries->getFont()->setSuperScript(true); - $oPie3D->addSeries($oSeries); - $oShape->getPlotArea()->setType($oPie3D); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/c:chartSpace/c:chart/c:plotArea/c:pie3DChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $this->assertEquals('30000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); - } - - public function testTypeScatter() - { - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oScatter = new Scatter(); - $oSeries = new Series('Downloads', $seriesData); - $oScatter->addSeries($oSeries); - $oShape->getPlotArea()->setType($oScatter); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); - $element = '/c:chartSpace/c:chart/c:plotArea/c:scatterChart'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $element = '/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:tx/c:v'; - $this->assertEquals($oSeries->getTitle(), $oXMLDoc->getElement($element, 'ppt/charts/'.$oShape->getIndexedFilename())->nodeValue); - } - - public function testTypeScatterSubScript() - { - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oScatter = new Scatter(); - $oSeries = new Series('Downloads', $seriesData); - $oSeries->getFont()->setSubScript(true); - $oScatter->addSeries($oSeries); - $oShape->getPlotArea()->setType($oScatter); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $this->assertEquals('-25000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); - } - - public function testTypeScatterSuperScript() - { - $seriesData = array( - 'A' => 1, - 'B' => 2, - 'C' => 4, - 'D' => 3, - 'E' => 2, - ); - - $oPhpPresentation = new PhpPresentation(); - $oSlide = $oPhpPresentation->getActiveSlide(); - $oShape = $oSlide->createChartShape(); - $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); - $oScatter = new Scatter(); - $oSeries = new Series('Downloads', $seriesData); - $oSeries->getFont()->setSuperScript(true); - $oScatter->addSeries($oSeries); - $oShape->getPlotArea()->setType($oScatter); - - $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - - $element = '/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; - $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); - $this->assertEquals('30000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); - } -} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/CommentAuthorsTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/CommentAuthorsTest.php new file mode 100644 index 000000000..a41fb8214 --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/CommentAuthorsTest.php @@ -0,0 +1,85 @@ +oPresentation = new PhpPresentation(); + } + + /** + * Executed before each method of the class + */ + public function tearDown() + { + $this->oPresentation = null; + TestHelperDOCX::clear(); + } + + public function testComments() + { + $expectedElement = '/p:cmAuthorLst/p:cmAuthor'; + $expectedName = 'Name'; + $expectedInitials = 'Initials'; + + $oAuthor = new Comment\Author(); + $oAuthor->setName($expectedName); + $oAuthor->setInitials($expectedInitials); + $oComment = new Comment(); + $oComment->setAuthor($oAuthor); + $this->oPresentation->getActiveSlide()->addShape($oComment); + + $pres = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($pres->fileExists('ppt/commentAuthors.xml')); + $this->assertTrue($pres->elementExists($expectedElement, 'ppt/commentAuthors.xml')); + $this->assertEquals(0, $pres->getElementAttribute($expectedElement, 'id', 'ppt/commentAuthors.xml')); + $this->assertEquals($expectedName, $pres->getElementAttribute($expectedElement, 'name', 'ppt/commentAuthors.xml')); + $this->assertEquals($expectedInitials, $pres->getElementAttribute($expectedElement, 'initials', 'ppt/commentAuthors.xml')); + } + + public function testWithoutComment() + { + $pres = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertFalse($pres->fileExists('ppt/commentAuthors.xml')); + } + + public function testWithoutCommentAuthor() + { + $oComment = new Comment(); + $this->oPresentation->getActiveSlide()->addShape($oComment); + + $pres = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertFalse($pres->fileExists('ppt/commentAuthors.xml')); + } + + public function testWithSameAuthor() + { + $expectedElement = '/p:cmAuthorLst/p:cmAuthor'; + + $oAuthor = new Comment\Author(); + + $oComment1 = new Comment(); + $oComment1->setAuthor($oAuthor); + $this->oPresentation->getActiveSlide()->addShape($oComment1); + $oComment2 = new Comment(); + $oComment2->setAuthor($oAuthor); + $this->oPresentation->getActiveSlide()->addShape($oComment2); + + $pres = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($pres->fileExists('ppt/commentAuthors.xml')); + $this->assertTrue($pres->fileExists('ppt/commentAuthors.xml')); + $this->assertTrue($pres->elementExists($expectedElement, 'ppt/commentAuthors.xml')); + $this->assertEquals(1, $pres->elementCount($expectedElement, 'ppt/commentAuthors.xml')); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/ContenTypesTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/ContenTypesTest.php deleted file mode 100644 index d1b4dce51..000000000 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/ContenTypesTest.php +++ /dev/null @@ -1,50 +0,0 @@ -setParentWriter(new ODPresentation()); - $oContentTypes->writeContentTypes(new PhpPresentation()); - } -} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsAppTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsAppTest.php new file mode 100644 index 000000000..517d228c6 --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsAppTest.php @@ -0,0 +1,44 @@ +assertTrue($oXMLDoc->fileExists('docProps/app.xml')); + } + + public function testCompany() + { + $expected = 'aAbBcDeE'; + + $oPhpPresentation = new PhpPresentation(); + $oPhpPresentation->getDocumentProperties()->setCompany($expected); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('docProps/app.xml')); + $this->assertTrue($oXMLDoc->elementExists('/Properties/Company', 'docProps/app.xml')); + $this->assertEquals($expected, $oXMLDoc->getElement('/Properties/Company', 'docProps/app.xml')->nodeValue); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsCoreTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsCoreTest.php new file mode 100644 index 000000000..9bc50082e --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsCoreTest.php @@ -0,0 +1,84 @@ +assertTrue($oXMLDoc->fileExists('docProps/core.xml')); + } + + public function testDocumentProperties() + { + $expected = 'aAbBcDeE'; + + $oPhpPresentation = new PhpPresentation(); + $oPhpPresentation->getDocumentProperties()->setCreator($expected); + $oPhpPresentation->getDocumentProperties()->setTitle($expected); + $oPhpPresentation->getDocumentProperties()->setDescription($expected); + $oPhpPresentation->getDocumentProperties()->setSubject($expected); + $oPhpPresentation->getDocumentProperties()->setKeywords($expected); + $oPhpPresentation->getDocumentProperties()->setCategory($expected); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('docProps/core.xml')); + + $this->assertTrue($oXMLDoc->elementExists('/cp:coreProperties/dc:creator', 'docProps/core.xml')); + $this->assertEquals($expected, $oXMLDoc->getElement('/cp:coreProperties/dc:creator', 'docProps/core.xml')->nodeValue); + + $this->assertTrue($oXMLDoc->elementExists('/cp:coreProperties/dc:title', 'docProps/core.xml')); + $this->assertEquals($expected, $oXMLDoc->getElement('/cp:coreProperties/dc:title', 'docProps/core.xml')->nodeValue); + + $this->assertTrue($oXMLDoc->elementExists('/cp:coreProperties/dc:description', 'docProps/core.xml')); + $this->assertEquals($expected, $oXMLDoc->getElement('/cp:coreProperties/dc:description', 'docProps/core.xml')->nodeValue); + + $this->assertTrue($oXMLDoc->elementExists('/cp:coreProperties/dc:subject', 'docProps/core.xml')); + $this->assertEquals($expected, $oXMLDoc->getElement('/cp:coreProperties/dc:subject', 'docProps/core.xml')->nodeValue); + + $this->assertTrue($oXMLDoc->elementExists('/cp:coreProperties/cp:keywords', 'docProps/core.xml')); + $this->assertEquals($expected, $oXMLDoc->getElement('/cp:coreProperties/cp:keywords', 'docProps/core.xml')->nodeValue); + + $this->assertTrue($oXMLDoc->elementExists('/cp:coreProperties/cp:category', 'docProps/core.xml')); + $this->assertEquals($expected, $oXMLDoc->getElement('/cp:coreProperties/cp:category', 'docProps/core.xml')->nodeValue); + } + + public function testMarkAsFinal() + { + $oPhpPresentation = new PhpPresentation(); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertFalse($pres->elementExists('/cp:coreProperties/cp:contentStatus', 'docProps/core.xml')); + + $oPhpPresentation->getPresentationProperties()->markAsFinal(true); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($pres->elementExists('/cp:coreProperties/cp:contentStatus', 'docProps/core.xml')); + $this->assertEquals('Final', $pres->getElement('/cp:coreProperties/cp:contentStatus', 'docProps/core.xml')->nodeValue); + + $oPhpPresentation->getPresentationProperties()->markAsFinal(false); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertFalse($pres->elementExists('/cp:coreProperties/cp:contentStatus', 'docProps/core.xml')); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsCustomTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsCustomTest.php new file mode 100644 index 000000000..33831d0cd --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsCustomTest.php @@ -0,0 +1,52 @@ +assertTrue($oXMLDoc->fileExists('docProps/custom.xml')); + } + + public function testMarkAsFinal() + { + $oPhpPresentation = new PhpPresentation(); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertFalse($pres->elementExists('/Properties/property[@name="_MarkAsFinal"]', 'docProps/custom.xml')); + + $oPhpPresentation->getPresentationProperties()->markAsFinal(true); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($pres->elementExists('/Properties', 'docProps/custom.xml')); + $this->assertTrue($pres->elementExists('/Properties/property', 'docProps/custom.xml')); + $this->assertTrue($pres->elementExists('/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="_MarkAsFinal"]', 'docProps/custom.xml')); + $this->assertTrue($pres->elementExists('/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="_MarkAsFinal"]/vt:bool', 'docProps/custom.xml')); + + $oPhpPresentation->getPresentationProperties()->markAsFinal(false); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertFalse($pres->elementExists('/Properties/property[@name="_MarkAsFinal"]', 'docProps/custom.xml')); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsThumbnailTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsThumbnailTest.php new file mode 100644 index 000000000..792ff72be --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsThumbnailTest.php @@ -0,0 +1,42 @@ +assertFalse($oXMLDoc->fileExists('docProps/thumbnail.jpeg')); + } + + public function testFeatureThumbnail() + { + $imagePath = PHPPRESENTATION_TESTS_BASE_DIR.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.'PhpPresentationLogo.png'; + + $oPhpPresentation = new PhpPresentation(); + $oPhpPresentation->getPresentationProperties()->setThumbnailPath($imagePath); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('docProps/thumbnail.jpeg')); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DrawingTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DrawingTest.php deleted file mode 100644 index d86f6849a..000000000 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DrawingTest.php +++ /dev/null @@ -1,55 +0,0 @@ -getActiveSlide(); - $oGroup = $oSlide->createGroup(); - - $oDrawing = new Drawing(); - $this->assertInternalType('array', $oDrawing->allDrawings($oPhpPresentation)); - $this->assertEmpty($oDrawing->allDrawings($oPhpPresentation)); - - $oGroup->createDrawingShape(); - $oGroup->createDrawingShape(); - $oGroup->createDrawingShape(); - - $this->assertInternalType('array', $oDrawing->allDrawings($oPhpPresentation)); - $this->assertCount(3, $oDrawing->allDrawings($oPhpPresentation)); - } -} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/LayoutPack/TemplateBasedTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/LayoutPack/TemplateBasedTest.php index 2ad2b261b..24002cd6b 100644 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/LayoutPack/TemplateBasedTest.php +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/LayoutPack/TemplateBasedTest.php @@ -23,6 +23,7 @@ /** * Test class for TemplateBased * + * @deprecated 0.7 * @coversDefaultClass TemplateBased */ class TemplateBasedTest extends \PHPUnit_Framework_TestCase diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptChartsTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptChartsTest.php new file mode 100644 index 000000000..05eda2bc2 --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptChartsTest.php @@ -0,0 +1,721 @@ + 1, + 'B' => 2, + 'C' => 4, + 'D' => 3, + 'E' => 2, + ); + /** + * Executed before each method of the class + */ + public function setUp() + { + $this->oPresentation = new PhpPresentation(); + } + + /** + * Executed after each method of the class + */ + public function tearDown() + { + TestHelperDOCX::clear(); + $this->oPresentation = null; + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage The chart type provided could not be rendered. + */ + public function testPlotAreaBadType() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $stub = $this->getMockForAbstractClass('PhpOffice\PhpPresentation\Shape\Chart\Type\AbstractType'); + $oShape->getPlotArea()->setType($stub); + + TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + } + + public function testTitleVisibility() + { + $element = '/c:chartSpace/c:chart/c:autoTitleDeleted'; + + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oLine = new Line(); + $oShape->getPlotArea()->setType($oLine); + + $this->assertTrue($oShape->getTitle()->isVisible()); + $this->assertInstanceOf('PhpOffice\PhpPresentation\Shape\Chart\Title', $oShape->getTitle()->setVisible(true)); + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals('0', $oXMLDoc->getElementAttribute($element, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); + + $this->assertInstanceOf('PhpOffice\PhpPresentation\Shape\Chart\Title', $oShape->getTitle()->setVisible(false)); + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals('1', $oXMLDoc->getElementAttribute($element, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testAxisFont() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oBar = new Bar(); + $oSeries = new Series('Downloads', $this->seriesData); + $oBar->addSeries($oSeries); + $oShape->getPlotArea()->setType($oBar); + $oShape->getPlotArea()->getAxisX()->getFont()->getColor()->setRGB('AABBCC'); + $oShape->getPlotArea()->getAxisX()->getFont()->setItalic(true); + $oShape->getPlotArea()->getAxisX()->getFont()->setStrikethrough(true); + $oShape->getPlotArea()->getAxisY()->getFont()->getColor()->setRGB('00FF00'); + $oShape->getPlotArea()->getAxisY()->getFont()->setSize(16); + $oShape->getPlotArea()->getAxisY()->getFont()->setUnderline(Font::UNDERLINE_DASH); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $element = '/c:chartSpace/c:chart/c:plotArea/c:catAx/c:txPr/a:p/a:pPr/a:defRPr/a:solidFill/a:srgbClr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals('AABBCC', $oXMLDoc->getElementAttribute($element, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); //Color XAxis + $element = '/c:chartSpace/c:chart/c:plotArea/c:catAx/c:txPr/a:p/a:pPr/a:defRPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals('true', $oXMLDoc->getElementAttribute($element, 'i', 'ppt/charts/'.$oShape->getIndexedFilename())); //Italic XAxis + $this->assertEquals(10*100, $oXMLDoc->getElementAttribute($element, 'sz', 'ppt/charts/'.$oShape->getIndexedFilename())); //Size XAxis + $this->assertEquals('sngStrike', $oXMLDoc->getElementAttribute($element, 'strike', 'ppt/charts/'.$oShape->getIndexedFilename())); //StrikeThrough XAxis + $this->assertEquals(Font::UNDERLINE_NONE, $oXMLDoc->getElementAttribute($element, 'u', 'ppt/charts/'.$oShape->getIndexedFilename())); //Underline XAxis + $element = '/c:chartSpace/c:chart/c:plotArea/c:valAx/c:txPr/a:p/a:pPr/a:defRPr/a:solidFill/a:srgbClr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals('00FF00', $oXMLDoc->getElementAttribute($element, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); //Color YAxis + $element = '/c:chartSpace/c:chart/c:plotArea/c:valAx/c:txPr/a:p/a:pPr/a:defRPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals('false', $oXMLDoc->getElementAttribute($element, 'i', 'ppt/charts/'.$oShape->getIndexedFilename())); //Italic YAxis + $this->assertEquals(16*100, $oXMLDoc->getElementAttribute($element, 'sz', 'ppt/charts/'.$oShape->getIndexedFilename())); //Size YAxis + $this->assertEquals('noStrike', $oXMLDoc->getElementAttribute($element, 'strike', 'ppt/charts/'.$oShape->getIndexedFilename())); //StrikeThrough YAxis + $this->assertEquals(Font::UNDERLINE_DASH, $oXMLDoc->getElementAttribute($element, 'u', 'ppt/charts/'.$oShape->getIndexedFilename())); //Underline YAxis + } + + public function testTypeArea() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oArea = new Area(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getFill()->setStartColor(new Color('FFAABBCC')); + $oArea->addSeries($oSeries); + $oShape->getPlotArea()->setType($oArea); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); + $element = '/c:chartSpace/c:chart/c:plotArea/c:areaChart'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:areaChart/c:ser'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testTypeBar() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oBar = new Bar(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_BLUE)); + $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKBLUE)); + $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKGREEN)); + $oSeries->getDataPointFill(3)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKRED)); + $oSeries->getDataPointFill(4)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKYELLOW)); + $oBar->addSeries($oSeries); + $oShape->getPlotArea()->setType($oBar); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); + $element = '/c:chartSpace/c:chart/c:plotArea/c:barChart'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser/c:dPt/c:spPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser/c:tx/c:v'; + $this->assertEquals($oSeries->getTitle(), $oXMLDoc->getElement($element, 'ppt/charts/'.$oShape->getIndexedFilename())->nodeValue); + } + + public function testTypeBar3D() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oBar3D = new Bar3D(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_BLUE)); + $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKBLUE)); + $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKGREEN)); + $oSeries->getDataPointFill(3)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKRED)); + $oSeries->getDataPointFill(4)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKYELLOW)); + $oBar3D->addSeries($oSeries); + $oShape->getPlotArea()->setType($oBar3D); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); + $element = '/c:chartSpace/c:chart/c:plotArea/c:bar3DChart'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:ser'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:ser/c:dPt/c:spPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:ser/c:tx/c:v'; + $this->assertEquals($oSeries->getTitle(), $oXMLDoc->getElement($element, 'ppt/charts/'.$oShape->getIndexedFilename())->nodeValue); + } + + public function testTypeBar3DSubScript() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oBar3D = new Bar3D(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getFont()->setSubScript(true); + $oBar3D->addSeries($oSeries); + $oShape->getPlotArea()->setType($oBar3D); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals('-25000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testTypeBar3DSuperScript() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oBar3D = new Bar3D(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getFont()->setSuperScript(true); + $oBar3D->addSeries($oSeries); + $oShape->getPlotArea()->setType($oBar3D); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals('30000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testTypeBar3DBarDirection() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oBar3D = new Bar3D(); + $oBar3D->setBarDirection(Bar3D::DIRECTION_HORIZONTAL); + $oSeries = new Series('Downloads', $this->seriesData); + $oBar3D->addSeries($oSeries); + $oShape->getPlotArea()->setType($oBar3D); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:barDir'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals(Bar3D::DIRECTION_HORIZONTAL, $oXMLDoc->getElementAttribute($element, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testTypeLine() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oLine = new Line(); + $oSeries = new Series('Downloads', $this->seriesData); + $oLine->addSeries($oSeries); + $oShape->getPlotArea()->setType($oLine); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); + $element = '/c:chartSpace/c:chart/c:plotArea/c:lineChart'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:lineChart/c:ser'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:lineChart/c:ser/c:tx/c:v'; + $this->assertEquals($oSeries->getTitle(), $oXMLDoc->getElement($element, 'ppt/charts/'.$oShape->getIndexedFilename())->nodeValue); + } + + public function testTypeLineGridlines() + { + $arrayTests = array( + array( + 'methodAxis' => 'getAxisX', + 'methodGrid' => 'setMajorGridlines', + 'expectedElement' => '/c:chartSpace/c:chart/c:plotArea/c:catAx/c:majorGridlines/c:spPr/a:ln', + 'expectedElementColor' => '/c:chartSpace/c:chart/c:plotArea/c:catAx/c:majorGridlines/c:spPr/a:ln/a:solidFill/a:srgbClr', + ), + array( + 'methodAxis' => 'getAxisX', + 'methodGrid' => 'setMinorGridlines', + 'expectedElement' => '/c:chartSpace/c:chart/c:plotArea/c:catAx/c:minorGridlines/c:spPr/a:ln', + 'expectedElementColor' => '/c:chartSpace/c:chart/c:plotArea/c:catAx/c:minorGridlines/c:spPr/a:ln/a:solidFill/a:srgbClr', + ), + array( + 'methodAxis' => 'getAxisY', + 'methodGrid' => 'setMajorGridlines', + 'expectedElement' => '/c:chartSpace/c:chart/c:plotArea/c:valAx/c:majorGridlines/c:spPr/a:ln', + 'expectedElementColor' => '/c:chartSpace/c:chart/c:plotArea/c:valAx/c:majorGridlines/c:spPr/a:ln/a:solidFill/a:srgbClr', + ), + array( + 'methodAxis' => 'getAxisY', + 'methodGrid' => 'setMinorGridlines', + 'expectedElement' => '/c:chartSpace/c:chart/c:plotArea/c:valAx/c:minorGridlines/c:spPr/a:ln', + 'expectedElementColor' => '/c:chartSpace/c:chart/c:plotArea/c:valAx/c:minorGridlines/c:spPr/a:ln/a:solidFill/a:srgbClr', + ), + ); + $expectedColor = new Color(Color::COLOR_BLUE); + foreach ($arrayTests as $arrayTest) { + $expectedSizePts = rand(1, 100); + $expectedSizeEmu = round(Drawing::pointsToPixels(Drawing::pixelsToEmu($expectedSizePts))); + + $oPresentation = new PhpPresentation(); + $oSlide = $oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oLine = new Line(); + $oLine->addSeries(new Series('Downloads', array( + 'A' => 1, + 'B' => 2, + 'C' => 4, + 'D' => 3, + 'E' => 2, + ))); + $oShape->getPlotArea()->setType($oLine); + $oGridlines = new Gridlines(); + $oGridlines->getOutline()->getFill()->setFillType(Fill::FILL_SOLID)->setStartColor($expectedColor); + $oGridlines->getOutline()->setWidth($expectedSizePts); + $oShape->getPlotArea()->{$arrayTest['methodAxis']}()->{$arrayTest['methodGrid']}($oGridlines); + + $oXMLDoc = TestHelperDOCX::getDocument($oPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertTrue($oXMLDoc->elementExists($arrayTest['expectedElement'], 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertTrue($oXMLDoc->attributeElementExists($arrayTest['expectedElement'], 'w', 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals($expectedSizeEmu, $oXMLDoc->getElementAttribute($arrayTest['expectedElement'], 'w', 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertTrue($oXMLDoc->elementExists($arrayTest['expectedElementColor'], 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertTrue($oXMLDoc->attributeElementExists($arrayTest['expectedElementColor'], 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals($expectedColor->getRGB(), $oXMLDoc->getElementAttribute($arrayTest['expectedElementColor'], 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); + + unset($oPresentation); + TestHelperDOCX::clear(); + } + } + + public function testTypeLineMarker() + { + do { + $expectedSymbol = array_rand(Marker::$arraySymbol); + } while ($expectedSymbol == Marker::SYMBOL_NONE); + $expectedSize = rand(2, 72); + $expectedEltSymbol = '/c:chartSpace/c:chart/c:plotArea/c:lineChart/c:ser/c:marker/c:symbol'; + $expectedElementSize = '/c:chartSpace/c:chart/c:plotArea/c:lineChart/c:ser/c:marker/c:size'; + + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oLine = new Line(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getMarker()->setSymbol($expectedSymbol)->setSize($expectedSize); + $oLine->addSeries($oSeries); + $oShape->getPlotArea()->setType($oLine); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->elementExists($expectedEltSymbol, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertTrue($oXMLDoc->elementExists($expectedElementSize, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals($expectedSymbol, $oXMLDoc->getElementAttribute($expectedEltSymbol, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals($expectedSize, $oXMLDoc->getElementAttribute($expectedElementSize, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); + + $oSeries->getMarker()->setSize(1); + $oLine->setSeries(array($oSeries)); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->elementExists($expectedElementSize, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals(2, $oXMLDoc->getElementAttribute($expectedElementSize, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); + + $oSeries->getMarker()->setSize(73); + $oLine->setSeries(array($oSeries)); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->elementExists($expectedElementSize, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals(72, $oXMLDoc->getElementAttribute($expectedElementSize, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); + + $oSeries->getMarker()->setSymbol(Marker::SYMBOL_NONE); + $oLine->setSeries(array($oSeries)); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertFalse($oXMLDoc->elementExists($expectedEltSymbol, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertFalse($oXMLDoc->elementExists($expectedElementSize, 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testTypeLineSeriesOutline() + { + $expectedWidth = rand(1, 100); + $expectedWidthEmu = Drawing::pixelsToEmu(Drawing::pointsToPixels($expectedWidth)); + $expectedElement = '/c:chartSpace/c:chart/c:plotArea/c:lineChart/c:ser/c:spPr/a:ln'; + + $oOutline = new Outline(); + // Define the color + $oOutline->getFill()->setFillType(Fill::FILL_SOLID); + $oOutline->getFill()->setStartColor(new Color(Color::COLOR_YELLOW)); + // Define the width (in points) + $oOutline->setWidth($expectedWidth); + + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oLine = new Line(); + $oSeries = new Series('Downloads', $this->seriesData); + $oLine->addSeries($oSeries); + $oShape->getPlotArea()->setType($oLine); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertFalse($oXMLDoc->elementExists($expectedElement, 'ppt/charts/'.$oShape->getIndexedFilename())); + + $oSeries->setOutline($oOutline); + $oLine->setSeries(array($oSeries)); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertTrue($oXMLDoc->elementExists($expectedElement, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals($expectedWidthEmu, $oXMLDoc->getElementAttribute($expectedElement, 'w', 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertTrue($oXMLDoc->elementExists($expectedElement.'/a:solidFill', 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testTypeLineSubScript() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oLine = new Line(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getFont()->setSubScript(true); + $oLine->addSeries($oSeries); + $oShape->getPlotArea()->setType($oLine); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/c:chartSpace/c:chart/c:plotArea/c:lineChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals('-25000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testTypeLineSuperScript() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oLine = new Line(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getFont()->setSuperScript(true); + $oLine->addSeries($oSeries); + $oShape->getPlotArea()->setType($oLine); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/c:chartSpace/c:chart/c:plotArea/c:lineChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals('30000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testTypePie() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oPie = new Pie(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_BLUE)); + $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKBLUE)); + $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKGREEN)); + $oSeries->getDataPointFill(3)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKRED)); + $oSeries->getDataPointFill(4)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKYELLOW)); + $oPie->addSeries($oSeries); + $oShape->getPlotArea()->setType($oPie); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); + $element = '/c:chartSpace/c:chart/c:plotArea/c:pieChart'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:pieChart/c:ser'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:pieChart/c:ser/c:dPt/c:spPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:pieChart/c:ser/c:tx/c:v'; + $this->assertEquals($oSeries->getTitle(), $oXMLDoc->getElement($element, 'ppt/charts/'.$oShape->getIndexedFilename())->nodeValue); + } + + public function testTypePie3D() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oPie3D = new Pie3D(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getDataPointFill(0)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_BLUE)); + $oSeries->getDataPointFill(1)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKBLUE)); + $oSeries->getDataPointFill(2)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKGREEN)); + $oSeries->getDataPointFill(3)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKRED)); + $oSeries->getDataPointFill(4)->setFillType(Fill::FILL_SOLID)->setStartColor(new Color(Color::COLOR_DARKYELLOW)); + $oPie3D->addSeries($oSeries); + $oShape->getPlotArea()->setType($oPie3D); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); + $element = '/c:chartSpace/c:chart/c:plotArea/c:pie3DChart'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:pie3DChart/c:ser'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:pie3DChart/c:ser/c:dPt/c:spPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:pie3DChart/c:ser/c:tx/c:v'; + $this->assertEquals($oSeries->getTitle(), $oXMLDoc->getElement($element, 'ppt/charts/'.$oShape->getIndexedFilename())->nodeValue); + } + + public function testTypePie3DExplosion() + { + $value = rand(1, 100); + + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oPie3D = new Pie3D(); + $oPie3D->setExplosion($value); + $oSeries = new Series('Downloads', $this->seriesData); + $oPie3D->addSeries($oSeries); + $oShape->getPlotArea()->setType($oPie3D); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/c:chartSpace/c:chart/c:plotArea/c:pie3DChart/c:ser/c:explosion'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals($value, $oXMLDoc->getElementAttribute($element, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testTypePie3DSubScript() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oPie3D = new Pie3D(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getFont()->setSubScript(true); + $oPie3D->addSeries($oSeries); + $oShape->getPlotArea()->setType($oPie3D); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/c:chartSpace/c:chart/c:plotArea/c:pie3DChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals('-25000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testTypePie3DSuperScript() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oPie3D = new Pie3D(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getFont()->setSuperScript(true); + $oPie3D->addSeries($oSeries); + $oShape->getPlotArea()->setType($oPie3D); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/c:chartSpace/c:chart/c:plotArea/c:pie3DChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals('30000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testTypeScatter() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oScatter = new Scatter(); + $oSeries = new Series('Downloads', $this->seriesData); + $oScatter->addSeries($oSeries); + $oShape->getPlotArea()->setType($oScatter); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); + $element = '/c:chartSpace/c:chart/c:plotArea/c:scatterChart'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $element = '/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:tx/c:v'; + $this->assertEquals($oSeries->getTitle(), $oXMLDoc->getElement($element, 'ppt/charts/'.$oShape->getIndexedFilename())->nodeValue); + } + + public function testTypeScatterMarker() + { + do { + $expectedSymbol = array_rand(Marker::$arraySymbol); + } while ($expectedSymbol == Marker::SYMBOL_NONE); + $expectedSize = rand(2, 72); + $expectedEltSymbol = '/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:marker/c:symbol'; + $expectedElementSize = '/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:marker/c:size'; + + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oScatter = new Scatter(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getMarker()->setSymbol($expectedSymbol)->setSize($expectedSize); + $oScatter->addSeries($oSeries); + $oShape->getPlotArea()->setType($oScatter); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->elementExists($expectedEltSymbol, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertTrue($oXMLDoc->elementExists($expectedElementSize, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals($expectedSymbol, $oXMLDoc->getElementAttribute($expectedEltSymbol, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals($expectedSize, $oXMLDoc->getElementAttribute($expectedElementSize, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); + + $oSeries->getMarker()->setSize(1); + $oScatter->setSeries(array($oSeries)); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->elementExists($expectedElementSize, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals(2, $oXMLDoc->getElementAttribute($expectedElementSize, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); + + $oSeries->getMarker()->setSize(73); + $oScatter->setSeries(array($oSeries)); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->elementExists($expectedElementSize, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals(72, $oXMLDoc->getElementAttribute($expectedElementSize, 'val', 'ppt/charts/'.$oShape->getIndexedFilename())); + + $oSeries->getMarker()->setSymbol(Marker::SYMBOL_NONE); + $oScatter->setSeries(array($oSeries)); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertFalse($oXMLDoc->elementExists($expectedEltSymbol, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertFalse($oXMLDoc->elementExists($expectedElementSize, 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testTypeScatterSeriesOutline() + { + $expectedWidth = rand(1, 100); + $expectedWidthEmu = Drawing::pixelsToEmu(Drawing::pointsToPixels($expectedWidth)); + $expectedElement = '/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:spPr/a:ln'; + + $oOutline = new Outline(); + // Define the color + $oOutline->getFill()->setFillType(Fill::FILL_SOLID); + $oOutline->getFill()->setStartColor(new Color(Color::COLOR_YELLOW)); + // Define the width (in points) + $oOutline->setWidth($expectedWidth); + + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oScatter = new Scatter(); + $oSeries = new Series('Downloads', $this->seriesData); + $oScatter->addSeries($oSeries); + $oShape->getPlotArea()->setType($oScatter); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertFalse($oXMLDoc->elementExists($expectedElement, 'ppt/charts/'.$oShape->getIndexedFilename())); + + $oSeries->setOutline($oOutline); + $oScatter->setSeries(array($oSeries)); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertTrue($oXMLDoc->elementExists($expectedElement, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals($expectedWidthEmu, $oXMLDoc->getElementAttribute($expectedElement, 'w', 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertTrue($oXMLDoc->elementExists($expectedElement.'/a:solidFill', 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testTypeScatterSubScript() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oScatter = new Scatter(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getFont()->setSubScript(true); + $oScatter->addSeries($oSeries); + $oShape->getPlotArea()->setType($oScatter); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals('-25000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); + } + + public function testTypeScatterSuperScript() + { + $oSlide = $this->oPresentation->getActiveSlide(); + $oShape = $oSlide->createChartShape(); + $oShape->setResizeProportional(false)->setHeight(550)->setWidth(700)->setOffsetX(120)->setOffsetY(80); + $oScatter = new Scatter(); + $oSeries = new Series('Downloads', $this->seriesData); + $oSeries->getFont()->setSuperScript(true); + $oScatter->addSeries($oSeries); + $oShape->getPlotArea()->setType($oScatter); + + $oXMLDoc = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + + $element = '/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:dLbls/c:txPr/a:p/a:pPr/a:defRPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/charts/'.$oShape->getIndexedFilename())); + $this->assertEquals('30000', $oXMLDoc->getElementAttribute($element, 'baseline', 'ppt/charts/'.$oShape->getIndexedFilename())); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptCommentsTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptCommentsTest.php new file mode 100644 index 000000000..683be4388 --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptCommentsTest.php @@ -0,0 +1,49 @@ +oPresentation = new PhpPresentation(); + } + + /** + * Executed before each method of the class + */ + public function tearDown() + { + $this->oPresentation = null; + TestHelperDOCX::clear(); + } + + public function testComments() + { + $expectedElement = '/p:cmLst/p:cm'; + + $oAuthor = new Comment\Author(); + $oComment = new Comment(); + $oComment->setAuthor($oAuthor); + $this->oPresentation->getActiveSlide()->addShape($oComment); + + $pres = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertTrue($pres->fileExists('ppt/comments/comment1.xml')); + $this->assertTrue($pres->elementExists($expectedElement, 'ppt/comments/comment1.xml')); + $this->assertEquals(0, $pres->getElementAttribute($expectedElement, 'authorId', 'ppt/comments/comment1.xml')); + } + public function testWithoutComment() + { + $pres = TestHelperDOCX::getDocument($this->oPresentation, 'PowerPoint2007'); + $this->assertFalse($pres->fileExists('ppt/comments/comment1.xml')); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptMediaTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptMediaTest.php new file mode 100644 index 000000000..83b1f0144 --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptMediaTest.php @@ -0,0 +1,121 @@ +getActiveSlide(); + $oShape = $oSlide->createDrawingShape(); + $oShape->setPath(PHPPRESENTATION_TESTS_BASE_DIR.'/resources/images/PhpPresentationLogo.png'); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('ppt/media/'.$oShape->getIndexedFilename())); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage does not exist + */ + public function testDrawingException() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oShape = $oSlide->createDrawingShape(); + $oShape->setPath(PHPPRESENTATION_TESTS_BASE_DIR.'/resources/images/filedoesntexist.png', false); + + TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + } + + public function testDrawingZip() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oDrawing = new Drawing\ZipFile(); + $oDrawing->setPath('zip://'.PHPPRESENTATION_TESTS_BASE_DIR.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'files'.DIRECTORY_SEPARATOR.'Sample_01_Simple.pptx#ppt/media/phppowerpoint_logo1.gif', false); + $oSlide->addShape($oDrawing); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('ppt/media/'.$oDrawing->getIndexedFilename())); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage does not exist + */ + public function testDrawingZipException() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + + $oDrawing = new Drawing\ZipFile(); + $oDrawing->setPath('zip://'.PHPPRESENTATION_TESTS_BASE_DIR.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'files'.DIRECTORY_SEPARATOR.'filedoesntexist.zip#secondpath', false); + + $oSlide->addShape($oDrawing); + + TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + } + + public function testDrawingBase64() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oShape = new Drawing\Base64(); + $oShape->setData('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIAtAFAAMBIgACEQEDEQH/xAAcAAEAAwEBAQEBAAAAAAAAAAAABAUGAwcCAQj/xABdEAABAwICAwYQCgcGBQIFBQEAAQIDBAUGERIhMQcTFkFR0RQVIjU2VFVhcXORkpOxssEXMjRSU3KBlKHSIzM3QlZ0giRioqPC4aSz0+LwQ2QlY2XD8SZFR4OERv/EABsBAQACAwEBAAAAAAAAAAAAAAADBQIEBgEH/8QAQREAAQMCBAMDCgUDAwQDAQEAAAECAwQREiExUQUTQRRhcSIyM1KBkaGxwfAVIzRC0TZy4QZT8RYkkqIlQ2KCNf/aAAwDAQACEQMRAD8A+eklo7l0f3dnMOklo7l0f3dnMTQddy2bIVeJ25C6SWjuXR/d2cw6SWjuXR/d2cxNA5bNkPMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7s5iaBy2bIMTtyF0ktHcuj+7s5h0ktHcuj+7t5iaBy2bIMTtzhT0VJSaXQtLDBp5aW9xo3PLlyO4BkiImh5cAAyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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', false); + $oSlide->addShape($oShape); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('ppt/media/'.$oShape->getIndexedFilename())); + } + + public function testMemoryDrawing() + { + $gdImage = imagecreatetruecolor(140, 20); + $textColor = imagecolorallocate($gdImage, 255, 255, 255); + imagestring($gdImage, 1, 5, 5, 'Created with PHPPresentation', $textColor); + + $oShape = new Drawing\Gd(); + $oShape->setImageResource($gdImage); + + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oSlide->addShape($oShape); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('ppt/media/'.$oShape->getIndexedFilename())); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptPresPropsTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptPresPropsTest.php new file mode 100644 index 000000000..466ab2245 --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptPresPropsTest.php @@ -0,0 +1,53 @@ +assertTrue($oXMLDoc->fileExists('ppt/presProps.xml')); + $element = '/p:presentationPr/p:extLst/p:ext'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/presProps.xml')); + $this->assertEquals('{E76CE94A-603C-4142-B9EB-6D1370010A27}', $oXMLDoc->getElementAttribute($element, 'uri', 'ppt/presProps.xml')); + } + + public function testLoopContinuously() + { + $oPhpPresentation = new PhpPresentation(); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('ppt/presProps.xml')); + $element = '/p:presentationPr/p:showPr'; + $this->assertFalse($oXMLDoc->elementExists($element, 'ppt/presProps.xml')); + + $oPhpPresentation->getPresentationProperties()->setLoopContinuouslyUntilEsc(true); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('ppt/presProps.xml')); + $element = '/p:presentationPr/p:showPr'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/presProps.xml')); + $this->assertTrue($oXMLDoc->attributeElementExists($element, 'loop', 'ppt/presProps.xml')); + $this->assertEquals(1, $oXMLDoc->getElementAttribute($element, 'loop', 'ppt/presProps.xml')); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptPresentationTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptPresentationTest.php new file mode 100644 index 000000000..c352d37de --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptPresentationTest.php @@ -0,0 +1,31 @@ +assertTrue($oXMLDoc->fileExists('ppt/presentation.xml')); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptPropsTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptPropsTest.php deleted file mode 100644 index 45f68477e..000000000 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptPropsTest.php +++ /dev/null @@ -1,68 +0,0 @@ -assertTrue($pres->elementExists($element, 'ppt/presProps.xml')); - $this->assertEquals('{E76CE94A-603C-4142-B9EB-6D1370010A27}', $pres->getElementAttribute($element, 'uri', 'ppt/presProps.xml')); - } - - public function testTableStyles() - { - $oPhpPresentation = new PhpPresentation(); - - $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - $element = '/a:tblStyleLst'; - $this->assertTrue($pres->elementExists($element, 'ppt/tableStyles.xml')); - $this->assertEquals('{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}', $pres->getElementAttribute($element, 'def', 'ppt/tableStyles.xml')); - } - - public function testViewProps() - { - $oPhpPresentation = new PhpPresentation(); - - $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - $element = '/p:viewPr'; - $this->assertTrue($pres->elementExists($element, 'ppt/viewProps.xml')); - $this->assertEquals('0', $pres->getElementAttribute($element, 'showComments', 'ppt/viewProps.xml')); - } -} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/SlideTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlidesTest.php similarity index 72% rename from tests/PhpPresentation/Tests/Writer/PowerPoint2007/SlideTest.php rename to tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlidesTest.php index 5e31db44e..2cbe15cbf 100644 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/SlideTest.php +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlidesTest.php @@ -18,15 +18,19 @@ namespace PhpOffice\PhpPresentation\Tests\Writer\PowerPoint2007; use PhpOffice\Common\Drawing; -use PhpOffice\PhpPresentation\Tests\TestHelperDOCX; use PhpOffice\PhpPresentation\PhpPresentation; +use PhpOffice\PhpPresentation\Shape\Comment; +use PhpOffice\PhpPresentation\Shape\Group; +use PhpOffice\PhpPresentation\Shape\Media; use PhpOffice\PhpPresentation\Shape\RichText; use PhpOffice\PhpPresentation\Style\Alignment; use PhpOffice\PhpPresentation\Style\Bullet; use PhpOffice\PhpPresentation\Style\Color; use PhpOffice\PhpPresentation\Style\Fill; +use PhpOffice\PhpPresentation\Slide\Animation; use PhpOffice\PhpPresentation\Slide\Transition; use PhpOffice\PhpPresentation\Style\Border; +use PhpOffice\PhpPresentation\Tests\TestHelperDOCX; use PhpOffice\PhpPresentation\Writer\PowerPoint2007; use PhpOffice\PhpPresentation\Writer\PowerPoint2007\Slide; @@ -44,16 +48,6 @@ public function tearDown() { TestHelperDOCX::clear(); } - - /** - * @expectedException \Exception - * @expectedExceptionMessage Invalid \PhpOffice\PhpPresentation\Slide object passed. - */ - public function testConstructException() - { - $oSlide = new Slide(); - $oSlide->writeSlide(); - } /** * @link https://github.com/PHPOffice/PHPPresentation/issues/42 @@ -65,13 +59,13 @@ public function testAlignmentShapeAuto() $oShape = $oSlide->createRichTextShape()->setWidth(400)->setHeight(400)->setOffsetX(100)->setOffsetY(100); $oShape->createTextRun('this text should be vertically aligned'); $oShape->getActiveParagraph()->getAlignment()->setVertical(Alignment::VERTICAL_AUTO); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:bodyPr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertFalse($pres->attributeElementExists($element, 'anchor', 'ppt/slides/slide1.xml')); } - + /** * @link https://github.com/PHPOffice/PHPPresentation/issues/42 */ @@ -82,13 +76,13 @@ public function testAlignmentShapeBase() $oShape = $oSlide->createRichTextShape()->setWidth(400)->setHeight(400)->setOffsetX(100)->setOffsetY(100); $oShape->createTextRun('this text should be vertically aligned'); $oShape->getActiveParagraph()->getAlignment()->setVertical(Alignment::VERTICAL_BASE); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:bodyPr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertFalse($pres->attributeElementExists($element, 'anchor', 'ppt/slides/slide1.xml')); } - + /** * @link https://github.com/PHPOffice/PHPPresentation/issues/35 */ @@ -99,13 +93,13 @@ public function testAlignmentShapeBottom() $oShape = $oSlide->createRichTextShape()->setWidth(400)->setHeight(400)->setOffsetX(100)->setOffsetY(100); $oShape->createTextRun('this text should be vertically aligned'); $oShape->getActiveParagraph()->getAlignment()->setVertical(Alignment::VERTICAL_BOTTOM); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:bodyPr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals(Alignment::VERTICAL_BOTTOM, $pres->getElementAttribute($element, 'anchor', 'ppt/slides/slide1.xml')); } - + /** * @link https://github.com/PHPOffice/PHPPresentation/issues/35 */ @@ -116,13 +110,13 @@ public function testAlignmentShapeCenter() $oShape = $oSlide->createRichTextShape()->setWidth(400)->setHeight(400)->setOffsetX(100)->setOffsetY(100); $oShape->createTextRun('this text should be vertically aligned'); $oShape->getActiveParagraph()->getAlignment()->setVertical(Alignment::VERTICAL_CENTER); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:bodyPr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals(Alignment::VERTICAL_CENTER, $pres->getElementAttribute($element, 'anchor', 'ppt/slides/slide1.xml')); } - + /** * @link https://github.com/PHPOffice/PHPPresentation/issues/35 */ @@ -133,62 +127,104 @@ public function testAlignmentShapeTop() $oShape = $oSlide->createRichTextShape()->setWidth(400)->setHeight(400)->setOffsetX(100)->setOffsetY(100); $oShape->createTextRun('this text should be vertically aligned'); $oShape->getActiveParagraph()->getAlignment()->setVertical(Alignment::VERTICAL_TOP); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:bodyPr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals(Alignment::VERTICAL_TOP, $pres->getElementAttribute($element, 'anchor', 'ppt/slides/slide1.xml')); } - + + public function testAnimation() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oShape1 = $oSlide->createRichTextShape(); + $oShape2 = $oSlide->createRichTextShape(); + $oAnimation = new Animation(); + $oAnimation->addShape($oShape1); + $oAnimation->addShape($oShape2); + $oSlide->addAnimation($oAnimation); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $element = '/p:sld/p:timing'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); + $element = '/p:sld/p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); + $element = '/p:sld/p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/slides/slide1.xml')); + } + + public function testCommentRelationship() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oSlide->addShape(new Comment()); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($pres->elementExists('/Relationships/Relationship[@Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"]', 'ppt/slides/_rels/slide1.xml.rels')); + } + + public function testCommentInGroupRelationship() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oGroup = new Group(); + $oGroup->addShape(new Comment()); + $oSlide->addShape($oGroup); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($pres->elementExists('/Relationships/Relationship[@Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"]', 'ppt/slides/_rels/slide1.xml.rels')); + } + public function testDrawingWithHyperlink() { $oPhpPresentation = new PhpPresentation(); $oSlide = $oPhpPresentation->getActiveSlide(); $oShape = $oSlide->createDrawingShape(); - $oShape->setPath(PHPPRESENTATION_TESTS_BASE_DIR.'/resources/images/PhpPresentationLogo.png'); + $oShape->setPath(PHPPRESENTATION_TESTS_BASE_DIR . '/resources/images/PhpPresentationLogo.png'); $oShape->getHyperlink()->setUrl('https://github.com/PHPOffice/PHPPresentation/'); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:pic/p:nvPicPr/p:cNvPr/a:hlinkClick'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); - $this->assertEquals('rId3', $pres->getElementAttribute($element, 'r:id', 'ppt/slides/slide1.xml')); + $this->assertEquals('rId2', $pres->getElementAttribute($element, 'r:id', 'ppt/slides/slide1.xml')); } - + public function testDrawingShapeBorder() { $oPhpPresentation = new PhpPresentation(); $oSlide = $oPhpPresentation->getActiveSlide(); $oShape = $oSlide->createDrawingShape(); - $oShape->setPath(PHPPRESENTATION_TESTS_BASE_DIR.'/resources/images/PhpPresentationLogo.png'); + $oShape->setPath(PHPPRESENTATION_TESTS_BASE_DIR . '/resources/images/PhpPresentationLogo.png'); $oShape->getBorder()->setLineStyle(Border::LINE_DOUBLE); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:pic/p:spPr/a:ln'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals(Border::LINE_DOUBLE, $pres->getElementAttribute($element, 'cmpd', 'ppt/slides/slide1.xml')); } - + public function testDrawingShapeShadow() { $oPhpPresentation = new PhpPresentation(); $oSlide = $oPhpPresentation->getActiveSlide(); $oShape = $oSlide->createDrawingShape(); - $oShape->setPath(PHPPRESENTATION_TESTS_BASE_DIR.'/resources/images/PhpPresentationLogo.png'); + $oShape->setPath(PHPPRESENTATION_TESTS_BASE_DIR . '/resources/images/PhpPresentationLogo.png'); $oShape->getShadow()->setVisible(true)->setDirection(45)->setDistance(10); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:pic/p:spPr/a:effectLst/a:outerShdw'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); } - + public function testFillGradientLinearTable() { $expected1 = 'E06B20'; $expected2 = strrev($expected1); - + $oPhpPresentation = new PhpPresentation(); $oSlide = $oPhpPresentation->getActiveSlide(); $oShape = $oSlide->createTableShape(1); @@ -197,10 +233,10 @@ public function testFillGradientLinearTable() $oCell = $oRow->getCell(); $oCell->createTextRun('R1C1'); $oFill = $oCell->getFill(); - $oFill->setFillType(Fill::FILL_GRADIENT_LINEAR)->setStartColor(new Color('FF'.$expected1))->setEndColor(new Color('FF'.$expected2)); - + $oFill->setFillType(Fill::FILL_GRADIENT_LINEAR)->setStartColor(new Color('FF' . $expected1))->setEndColor(new Color('FF' . $expected2)); + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tr/a:tc/a:tcPr/a:gradFill/a:gsLst/a:gs[@pos="0"]/a:srgbClr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals($expected1, $pres->getElementAttribute($element, 'val', 'ppt/slides/slide1.xml')); @@ -208,7 +244,7 @@ public function testFillGradientLinearTable() $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals($expected2, $pres->getElementAttribute($element, 'val', 'ppt/slides/slide1.xml')); } - + /** * @link : https://github.com/PHPOffice/PHPPresentation/issues/61 */ @@ -216,16 +252,16 @@ public function testFillGradientLinearRichText() { $expected1 = 'E06B20'; $expected2 = strrev($expected1); - + $oPhpPresentation = new PhpPresentation(); $oSlide = $oPhpPresentation->getActiveSlide(); $oShape = $oSlide->createRichTextShape(); $oShape->setHeight(200)->setWidth(600)->setOffsetX(150)->setOffsetY(300); $oFill = $oShape->getFill(); - $oFill->setFillType(Fill::FILL_GRADIENT_LINEAR)->setStartColor(new Color('FF'.$expected1))->setEndColor(new Color('FF'.$expected2)); - + $oFill->setFillType(Fill::FILL_GRADIENT_LINEAR)->setStartColor(new Color('FF' . $expected1))->setEndColor(new Color('FF' . $expected2)); + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:gradFill/a:gsLst/a:gs[@pos="0"]/a:srgbClr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals($expected1, $pres->getElementAttribute($element, 'val', 'ppt/slides/slide1.xml')); @@ -233,12 +269,12 @@ public function testFillGradientLinearRichText() $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals($expected2, $pres->getElementAttribute($element, 'val', 'ppt/slides/slide1.xml')); } - + public function testFillGradientPathTable() { $expected1 = 'E06B20'; $expected2 = strrev($expected1); - + $oPhpPresentation = new PhpPresentation(); $oSlide = $oPhpPresentation->getActiveSlide(); $oShape = $oSlide->createTableShape(1); @@ -247,10 +283,10 @@ public function testFillGradientPathTable() $oCell = $oRow->getCell(); $oCell->createTextRun('R1C1'); $oFill = $oCell->getFill(); - $oFill->setFillType(Fill::FILL_GRADIENT_PATH)->setStartColor(new Color('FF'.$expected1))->setEndColor(new Color('FF'.$expected2)); - + $oFill->setFillType(Fill::FILL_GRADIENT_PATH)->setStartColor(new Color('FF' . $expected1))->setEndColor(new Color('FF' . $expected2)); + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tr/a:tc/a:tcPr/a:gradFill/a:gsLst/a:gs[@pos="0"]/a:srgbClr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals($expected1, $pres->getElementAttribute($element, 'val', 'ppt/slides/slide1.xml')); @@ -258,7 +294,7 @@ public function testFillGradientPathTable() $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals($expected2, $pres->getElementAttribute($element, 'val', 'ppt/slides/slide1.xml')); } - + /** * @link : https://github.com/PHPOffice/PHPPresentation/issues/61 */ @@ -266,16 +302,16 @@ public function testFillGradientPathText() { $expected1 = 'E06B20'; $expected2 = strrev($expected1); - + $oPhpPresentation = new PhpPresentation(); $oSlide = $oPhpPresentation->getActiveSlide(); $oShape = $oSlide->createRichTextShape(); $oShape->setHeight(200)->setWidth(600)->setOffsetX(150)->setOffsetY(300); $oFill = $oShape->getFill(); - $oFill->setFillType(Fill::FILL_GRADIENT_PATH)->setStartColor(new Color('FF'.$expected1))->setEndColor(new Color('FF'.$expected2)); - + $oFill->setFillType(Fill::FILL_GRADIENT_PATH)->setStartColor(new Color('FF' . $expected1))->setEndColor(new Color('FF' . $expected2)); + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:gradFill/a:gsLst/a:gs[@pos="0"]/a:srgbClr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals($expected1, $pres->getElementAttribute($element, 'val', 'ppt/slides/slide1.xml')); @@ -283,12 +319,12 @@ public function testFillGradientPathText() $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals($expected2, $pres->getElementAttribute($element, 'val', 'ppt/slides/slide1.xml')); } - + public function testFillPatternTable() { $expected1 = 'E06B20'; $expected2 = strrev($expected1); - + $oPhpPresentation = new PhpPresentation(); $oSlide = $oPhpPresentation->getActiveSlide(); $oShape = $oSlide->createTableShape(1); @@ -297,10 +333,10 @@ public function testFillPatternTable() $oCell = $oRow->getCell(); $oCell->createTextRun('R1C1'); $oFill = $oCell->getFill(); - $oFill->setFillType(Fill::FILL_PATTERN_LIGHTTRELLIS)->setStartColor(new Color('FF'.$expected1))->setEndColor(new Color('FF'.$expected2)); - + $oFill->setFillType(Fill::FILL_PATTERN_LIGHTTRELLIS)->setStartColor(new Color('FF' . $expected1))->setEndColor(new Color('FF' . $expected2)); + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tr/a:tc/a:tcPr/a:pattFill/a:fgClr/a:srgbClr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals($expected1, $pres->getElementAttribute($element, 'val', 'ppt/slides/slide1.xml')); @@ -308,11 +344,11 @@ public function testFillPatternTable() $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals($expected2, $pres->getElementAttribute($element, 'val', 'ppt/slides/slide1.xml')); } - + public function testFillSolidTable() { $expected = 'E06B20'; - + $oPhpPresentation = new PhpPresentation(); $oSlide = $oPhpPresentation->getActiveSlide(); $oShape = $oSlide->createTableShape(1); @@ -321,10 +357,10 @@ public function testFillSolidTable() $oCell = $oRow->getCell(); $oCell->createTextRun('R1C1'); $oFill = $oCell->getFill(); - $oFill->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF'.$expected)); - + $oFill->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF' . $expected)); + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tr/a:tc/a:tcPr/a:solidFill/a:srgbClr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals($expected, $pres->getElementAttribute($element, 'val', 'ppt/slides/slide1.xml')); @@ -336,21 +372,21 @@ public function testFillSolidTable() public function testFillSolidText() { $expected = 'E06B20'; - + $oPhpPresentation = new PhpPresentation(); $oSlide = $oPhpPresentation->getActiveSlide(); $oShape = $oSlide->createRichTextShape(); $oShape->setHeight(200)->setWidth(600)->setOffsetX(150)->setOffsetY(300); $oFill = $oShape->getFill(); - $oFill->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF'.$expected)); - + $oFill->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FF' . $expected)); + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:solidFill/a:srgbClr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals($expected, $pres->getElementAttribute($element, 'val', 'ppt/slides/slide1.xml')); } - + public function testHyperlink() { $oPhpPresentation = new PhpPresentation(); @@ -358,13 +394,13 @@ public function testHyperlink() $oRichText = $oSlide->createRichTextShape(); $oRun = $oRichText->createTextRun('Delta'); $oRun->getHyperlink()->setUrl('http://www.google.fr'); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr/a:hlinkClick'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); } - + public function testHyperlinkInternal() { $oPhpPresentation = new PhpPresentation(); @@ -372,9 +408,9 @@ public function testHyperlinkInternal() $oRichText = $oSlide->createRichTextShape(); $oRun = $oRichText->createTextRun('Delta'); $oRun->getHyperlink()->setSlideNumber(1); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr/a:hlinkClick'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals('ppaction://hlinksldjump', $pres->getElementAttribute($element, 'action', 'ppt/slides/slide1.xml')); @@ -386,20 +422,31 @@ public function testListBullet() $oSlide = $oPhpPresentation->getActiveSlide(); $oRichText = $oSlide->createRichTextShape(); $oRichText->getActiveParagraph()->getBulletStyle()->setBulletType(Bullet::TYPE_BULLET); + $oRichText->getActiveParagraph()->getBulletStyle()->setBulletColor(new Color('76543210')); + $oExpectedFont = $oRichText->getActiveParagraph()->getBulletStyle()->getBulletFont(); $oExpectedChar = $oRichText->getActiveParagraph()->getBulletStyle()->getBulletChar(); + $oExpectedColor = $oRichText->getActiveParagraph()->getBulletStyle()->getBulletColor()->getRGB(); + $oExpectedAlpha = $oRichText->getActiveParagraph()->getBulletStyle()->getBulletColor()->getAlpha(); + $oExpectedAlpha *= 1000; + $oRichText->createTextRun('Alpha'); $oRichText->createParagraph()->createTextRun('Beta'); $oRichText->createParagraph()->createTextRun('Delta'); $oRichText->createParagraph()->createTextRun('Epsilon'); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:pPr'; - $this->assertTrue($pres->elementExists($element.'/a:buFont', 'ppt/slides/slide1.xml')); - $this->assertEquals($oExpectedFont, $pres->getElementAttribute($element.'/a:buFont', 'typeface', 'ppt/slides/slide1.xml')); - $this->assertTrue($pres->elementExists($element.'/a:buChar', 'ppt/slides/slide1.xml')); - $this->assertEquals($oExpectedChar, $pres->getElementAttribute($element.'/a:buChar', 'char', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:buFont', 'ppt/slides/slide1.xml')); + $this->assertEquals($oExpectedFont, $pres->getElementAttribute($element . '/a:buFont', 'typeface', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:buChar', 'ppt/slides/slide1.xml')); + $this->assertEquals($oExpectedChar, $pres->getElementAttribute($element . '/a:buChar', 'char', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:buClr', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:buClr/a:srgbClr', 'ppt/slides/slide1.xml')); + $this->assertEquals($oExpectedColor, $pres->getElementAttribute($element . '/a:buClr/a:srgbClr', 'val', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:buClr/a:srgbClr/a:alpha', 'ppt/slides/slide1.xml')); + $this->assertEquals($oExpectedAlpha, $pres->getElementAttribute($element . '/a:buClr/a:srgbClr/a:alpha', 'val', 'ppt/slides/slide1.xml')); } public function testListNumeric() @@ -417,53 +464,91 @@ public function testListNumeric() $oRichText->createParagraph()->createTextRun('Beta'); $oRichText->createParagraph()->createTextRun('Delta'); $oRichText->createParagraph()->createTextRun('Epsilon'); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:pPr'; - $this->assertTrue($pres->elementExists($element.'/a:buFont', 'ppt/slides/slide1.xml')); - $this->assertEquals($oExpectedFont, $pres->getElementAttribute($element.'/a:buFont', 'typeface', 'ppt/slides/slide1.xml')); - $this->assertTrue($pres->elementExists($element.'/a:buAutoNum', 'ppt/slides/slide1.xml')); - $this->assertEquals($oExpectedChar, $pres->getElementAttribute($element.'/a:buAutoNum', 'type', 'ppt/slides/slide1.xml')); - $this->assertEquals($oExpectedStart, $pres->getElementAttribute($element.'/a:buAutoNum', 'startAt', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:buFont', 'ppt/slides/slide1.xml')); + $this->assertEquals($oExpectedFont, $pres->getElementAttribute($element . '/a:buFont', 'typeface', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:buAutoNum', 'ppt/slides/slide1.xml')); + $this->assertEquals($oExpectedChar, $pres->getElementAttribute($element . '/a:buAutoNum', 'type', 'ppt/slides/slide1.xml')); + $this->assertEquals($oExpectedStart, $pres->getElementAttribute($element . '/a:buAutoNum', 'startAt', 'ppt/slides/slide1.xml')); } - + public function testLine() { $valEmu10 = Drawing::pixelsToEmu(10); $valEmu90 = Drawing::pixelsToEmu(90); - + $oPhpPresentation = new PhpPresentation(); $oSlide = $oPhpPresentation->getActiveSlide(); $oSlide->createLineShape(10, 10, 100, 100); $oSlide->createLineShape(100, 10, 10, 100); $oSlide->createLineShape(10, 100, 100, 10); $oSlide->createLineShape(100, 100, 10, 10); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:cxnSp/p:spPr/a:prstGeom'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals('line', $pres->getElementAttribute($element, 'prst', 'ppt/slides/slide1.xml')); - - $element = '/p:sld/p:cSld/p:spTree/p:cxnSp/p:spPr/a:xfrm/a:ext[@cx="'.$valEmu90.'"][@cy="'.$valEmu90.'"]'; + + $element = '/p:sld/p:cSld/p:spTree/p:cxnSp/p:spPr/a:xfrm/a:ext[@cx="' . $valEmu90 . '"][@cy="' . $valEmu90 . '"]'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); - - $element = '/p:sld/p:cSld/p:spTree/p:cxnSp/p:spPr/a:xfrm/a:off[@x="'.$valEmu10.'"][@y="'.$valEmu10.'"]'; + + $element = '/p:sld/p:cSld/p:spTree/p:cxnSp/p:spPr/a:xfrm/a:off[@x="' . $valEmu10 . '"][@y="' . $valEmu10 . '"]'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); - - $element = '/p:sld/p:cSld/p:spTree/p:cxnSp/p:spPr/a:xfrm[@flipV="1"]/a:off[@x="'.$valEmu10.'"][@y="'.$valEmu10.'"]'; + + $element = '/p:sld/p:cSld/p:spTree/p:cxnSp/p:spPr/a:xfrm[@flipV="1"]/a:off[@x="' . $valEmu10 . '"][@y="' . $valEmu10 . '"]'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); } + public function testMedia() + { + $expectedName = 'MyName'; + $expectedWidth = rand(1, 100); + $expectedHeight = rand(1, 100); + $expectedX = rand(1, 100); + $expectedY = rand(1, 100); + + $oMedia = new Media(); + $oMedia->setPath(PHPPRESENTATION_TESTS_BASE_DIR . '/resources/videos/sintel_trailer-480p.ogv') + ->setName($expectedName) + ->setResizeProportional(false) + ->setHeight($expectedHeight) + ->setWidth($expectedWidth) + ->setOffsetX($expectedX) + ->setOffsetY($expectedY); + + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oSlide->addShape($oMedia); + + $xmlObject = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $element = '/p:sld/p:cSld/p:spTree/p:pic/p:nvPicPr/p:cNvPr'; + $this->assertTrue($xmlObject->elementExists($element, 'ppt/slides/slide1.xml')); + $this->assertEquals($expectedName, $xmlObject->getElementAttribute($element, 'name', 'ppt/slides/slide1.xml')); + + $element = '/p:sld/p:cSld/p:spTree/p:pic/p:nvPicPr/p:nvPr/a:videoFile'; + $this->assertTrue($xmlObject->elementExists($element, 'ppt/slides/slide1.xml')); + $element = '/p:sld/p:cSld/p:spTree/p:pic/p:nvPicPr/p:nvPr/p:extLst/p:ext'; + $this->assertTrue($xmlObject->elementExists($element, 'ppt/slides/slide1.xml')); + $this->assertEquals('{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}', $xmlObject->getElementAttribute($element, 'uri', 'ppt/slides/slide1.xml')); + } + public function testNote() { $oPhpPresentation = new PhpPresentation(); + $oLayout = $oPhpPresentation->getLayout(); $oSlide = $oPhpPresentation->getActiveSlide(); $oNote = $oSlide->getNote(); - $oRichText = $oNote->createRichTextShape()->setHeight(300)->setWidth(600); + $oRichText = $oNote->createRichTextShape() + ->setHeight($oLayout->getCY($oLayout::UNIT_PIXEL)) + ->setWidth($oLayout->getCX($oLayout::UNIT_PIXEL)) + ->setOffsetX(170) + ->setOffsetY(180); $oRichText->createTextRun('testNote'); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); // Content Types // $element = '/Types/Override[@PartName="/ppt/notesSlides/notesSlide1.xml"][@ContentType="application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml"]'; @@ -472,12 +557,34 @@ public function testNote() // $element = '/Relationships/Relationship[@Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide"][@Target="../notesSlides/notesSlide1.xml"]'; // $this->assertTrue($pres->elementExists($element, 'ppt/slides/_rels/slide1.xml.rels')); // Slide + $element = '/p:notes'; $this->assertTrue($pres->elementExists($element, 'ppt/notesSlides/notesSlide1.xml')); + // Slide Image Placeholder 1 + $element = '/p:notes/p:cSld/p:spTree/p:sp/p:nvSpPr/p:cNvPr[@id="2"][@name="Slide Image Placeholder 1"]'; + $this->assertTrue($pres->elementExists($element, 'ppt/notesSlides/notesSlide1.xml')); + $element = '/p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:xfrm/a:off'; + $this->assertEquals(0, $pres->getElementAttribute($element, 'x', 'ppt/notesSlides/notesSlide1.xml')); + $this->assertEquals(0, $pres->getElementAttribute($element, 'y', 'ppt/notesSlides/notesSlide1.xml')); + $element = '/p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:xfrm/a:ext'; + $this->assertEquals(Drawing::pixelsToEmu(round($oNote->getExtentX() / 2)), $pres->getElementAttribute($element, 'cx', 'ppt/notesSlides/notesSlide1.xml')); + $this->assertEquals(Drawing::pixelsToEmu(round($oNote->getExtentY() / 2)), $pres->getElementAttribute($element, 'cy', 'ppt/notesSlides/notesSlide1.xml')); + + // Notes Placeholder + $element = '/p:notes/p:cSld/p:spTree/p:sp/p:nvSpPr/p:cNvPr[@id="3"][@name="Notes Placeholder"]'; + $this->assertTrue($pres->elementExists($element, 'ppt/notesSlides/notesSlide1.xml')); + + // Notes + $element = '/p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:xfrm/a:off'; + $this->assertEquals(Drawing::pixelsToEmu($oNote->getOffsetX()), $pres->getElementAttribute($element, 'x', 'ppt/notesSlides/notesSlide1.xml')); + $this->assertEquals(Drawing::pixelsToEmu(round($oNote->getExtentY() / 2) + $oNote->getOffsetY()), $pres->getElementAttribute($element, 'y', 'ppt/notesSlides/notesSlide1.xml')); + $element = '/p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:xfrm/a:ext'; + $this->assertEquals(5486400, $pres->getElementAttribute($element, 'cx', 'ppt/notesSlides/notesSlide1.xml')); + $this->assertEquals(3600450, $pres->getElementAttribute($element, 'cy', 'ppt/notesSlides/notesSlide1.xml')); $element = '/p:notes/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:t'; $this->assertTrue($pres->elementExists($element, 'ppt/notesSlides/notesSlide1.xml')); } - + public function testRichTextAutoFitNormal() { $oPhpPresentation = new PhpPresentation(); @@ -485,27 +592,27 @@ public function testRichTextAutoFitNormal() $oRichText = $oSlide->createRichTextShape(); $oRichText->setAutoFit(RichText::AUTOFIT_NORMAL, 47.5, 20); $oRichText->createTextRun('This is my text for the test.'); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:bodyPr/a:normAutofit'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals(47500, $pres->getElementAttribute($element, 'fontScale', 'ppt/slides/slide1.xml')); $this->assertEquals(20000, $pres->getElementAttribute($element, 'lnSpcReduction', 'ppt/slides/slide1.xml')); } - + public function testRichTextBreak() { $oPhpPresentation = new PhpPresentation(); $oSlide = $oPhpPresentation->getActiveSlide(); $oRichText = $oSlide->createRichTextShape(); $oRichText->createBreak(); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:br'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); } - + public function testRichTextHyperlink() { $oPhpPresentation = new PhpPresentation(); @@ -518,7 +625,46 @@ public function testRichTextHyperlink() $element = '/p:sld/p:cSld/p:spTree/p:sp//a:hlinkClick'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); } - + + public function testRichTextLineSpacing() + { + $expectedLineSpacing = rand(1, 100); + + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oRichText = $oSlide->createRichTextShape(); + $oRichText->createTextRun('AAA'); + $oRichText->getActiveParagraph()->setLineSpacing($expectedLineSpacing); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + + $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:pPr/a:lnSpc/a:spcPct'; + $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); + $this->assertEquals($expectedLineSpacing * 1000, $pres->getElementAttribute($element, 'val', 'ppt/slides/slide1.xml')); + } + + public function testRichTextRunLanguage() + { + $oPhpPresentation = new PhpPresentation(); + $oSlide = $oPhpPresentation->getActiveSlide(); + $oRichText = $oSlide->createRichTextShape(); + $oRun = $oRichText->createTextRun('MyText'); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $expectedElement = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr'; + $this->assertTrue($pres->elementExists($expectedElement, 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->attributeElementExists($expectedElement, 'lang', 'ppt/slides/slide1.xml')); + $this->assertEquals('en-US', $pres->getElementAttribute($expectedElement, 'lang', 'ppt/slides/slide1.xml')); + + $oRun->setLanguage('de_DE'); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $expectedElement = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr'; + $this->assertTrue($pres->elementExists($expectedElement, 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->attributeElementExists($expectedElement, 'lang', 'ppt/slides/slide1.xml')); + $this->assertEquals('de_DE', $pres->getElementAttribute($expectedElement, 'lang', 'ppt/slides/slide1.xml')); + } + public function testRichTextShadow() { $oPhpPresentation = new PhpPresentation(); @@ -526,13 +672,13 @@ public function testRichTextShadow() $oRichText = $oSlide->createRichTextShape(); $oRichText->createTextRun('AAA'); $oRichText->getShadow()->setVisible(true)->setAlpha(75)->setBlurRadius(2)->setDirection(45); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:effectLst/a:outerShdw'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); } - + public function testRichTextUpright() { $oPhpPresentation = new PhpPresentation(); @@ -540,14 +686,14 @@ public function testRichTextUpright() $oRichText = $oSlide->createRichTextShape(); $oRichText->createTextRun('AAA'); $oRichText->setUpright(true); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:bodyPr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals('1', $pres->getElementAttribute($element, 'upright', 'ppt/slides/slide1.xml')); } - + public function testRichTextVertical() { $oPhpPresentation = new PhpPresentation(); @@ -555,9 +701,9 @@ public function testRichTextVertical() $oRichText = $oSlide->createRichTextShape(); $oRichText->createTextRun('AAA'); $oRichText->setVertical(true); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:bodyPr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals('vert', $pres->getElementAttribute($element, 'vert', 'ppt/slides/slide1.xml')); @@ -570,9 +716,9 @@ public function testStyleSubScript() $oRichText = $oSlide->createRichTextShape(); $oRun = $oRichText->createTextRun('pText'); $oRun->getFont()->setSubScript(true); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals('-25000', $pres->getElementAttribute($element, 'baseline', 'ppt/slides/slide1.xml')); @@ -585,9 +731,9 @@ public function testStyleSuperScript() $oRichText = $oSlide->createRichTextShape(); $oRun = $oRichText->createTextRun('pText'); $oRun->getFont()->setSuperScript(true); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals('30000', $pres->getElementAttribute($element, 'baseline', 'ppt/slides/slide1.xml')); @@ -603,9 +749,9 @@ public function testTableWithAlignment() $oCell = $oRow->getCell(); $oCell->createTextRun('AAA'); $oCell->getActiveParagraph()->getAlignment()->setVertical(Alignment::VERTICAL_BOTTOM); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tr/a:tc/a:tcPr'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals(Alignment::VERTICAL_BOTTOM, $pres->getElementAttribute($element, 'anchor', 'ppt/slides/slide1.xml')); @@ -637,18 +783,18 @@ public function testTableWithBorder() $oCell->createTextRun('CCC'); $oCell->getBorders()->getBottom()->setDashStyle(Border::DASH_LARGEDASHDOTDOT); $oCell->getBorders()->getBottom()->setLineStyle(Border::LINE_NONE); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tr/a:tc/a:tcPr'; - $this->assertTrue($pres->elementExists($element.'/a:lnL[@cmpd="'.Border::LINE_THINTHICK.'"]', 'ppt/slides/slide1.xml')); - $this->assertTrue($pres->elementExists($element.'/a:lnL[@cmpd="'.Border::LINE_THINTHICK.'"]/a:prstDash[@val="'.Border::DASH_LARGEDASH.'"]', 'ppt/slides/slide1.xml')); - $this->assertTrue($pres->elementExists($element.'/a:lnR[@cmpd="'.Border::LINE_THICKTHIN.'"]', 'ppt/slides/slide1.xml')); - $this->assertTrue($pres->elementExists($element.'/a:lnR[@cmpd="'.Border::LINE_THICKTHIN.'"]/a:prstDash[@val="'.Border::DASH_DOT.'"]', 'ppt/slides/slide1.xml')); - $this->assertTrue($pres->elementExists($element.'/a:lnT[@cmpd="'.Border::LINE_SINGLE.'"]', 'ppt/slides/slide1.xml')); - $this->assertTrue($pres->elementExists($element.'/a:lnT[@cmpd="'.Border::LINE_SINGLE.'"]/a:prstDash[@val="'.Border::DASH_DASHDOT.'"]', 'ppt/slides/slide1.xml')); - $this->assertTrue($pres->elementExists($element.'/a:lnB[@cmpd="'.Border::LINE_SINGLE.'"]', 'ppt/slides/slide1.xml')); - $this->assertTrue($pres->elementExists($element.'/a:lnB[@cmpd="'.Border::LINE_SINGLE.'"]/a:prstDash[@val="'.Border::DASH_SOLID.'"]', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:lnL[@cmpd="' . Border::LINE_THINTHICK . '"]', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:lnL[@cmpd="' . Border::LINE_THINTHICK . '"]/a:prstDash[@val="' . Border::DASH_LARGEDASH . '"]', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:lnR[@cmpd="' . Border::LINE_THICKTHIN . '"]', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:lnR[@cmpd="' . Border::LINE_THICKTHIN . '"]/a:prstDash[@val="' . Border::DASH_DOT . '"]', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:lnT[@cmpd="' . Border::LINE_SINGLE . '"]', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:lnT[@cmpd="' . Border::LINE_SINGLE . '"]/a:prstDash[@val="' . Border::DASH_DASHDOT . '"]', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:lnB[@cmpd="' . Border::LINE_SINGLE . '"]', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/a:lnB[@cmpd="' . Border::LINE_SINGLE . '"]/a:prstDash[@val="' . Border::DASH_SOLID . '"]', 'ppt/slides/slide1.xml')); } public function testTableWithColspan() @@ -661,9 +807,9 @@ public function testTableWithColspan() $oCell = $oRow->getCell(); $oCell->createTextRun('AAA'); $oCell->setColSpan(2); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tr/a:tc'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); $this->assertEquals(2, $pres->getElementAttribute($element, 'gridSpan', 'ppt/slides/slide1.xml')); @@ -683,12 +829,12 @@ public function testTableWithRowspan() $oRow = $oShape->createRow(); $oCell = $oRow->getCell(); $oCell->createTextRun('BBB'); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tr/a:tc'; - $this->assertTrue($pres->elementExists($element.'[@rowSpan="2"]', 'ppt/slides/slide1.xml')); - $this->assertTrue($pres->elementExists($element.'[@vMerge="1"]', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '[@rowSpan="2"]', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '[@vMerge="1"]', 'ppt/slides/slide1.xml')); } /** @@ -705,12 +851,12 @@ public function testTableWithHyperlink() $oTextRun = $oCell->createTextRun('AAA'); $oHyperlink = $oTextRun->getHyperlink(); $oHyperlink->setUrl('https://github.com/PHPOffice/PHPPresentation/'); - + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - + $element = '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tr/a:tc/a:txBody/a:p/a:r/a:rPr/a:hlinkClick'; $this->assertTrue($pres->elementExists($element, 'ppt/slides/slide1.xml')); - $this->assertEquals('rId2', $pres->getElementAttribute($element, 'r:id', 'ppt/slides/slide1.xml')); + $this->assertEquals('rId1', $pres->getElementAttribute($element, 'r:id', 'ppt/slides/slide1.xml')); } public function testTransition() @@ -760,148 +906,148 @@ public function testTransition() $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); switch ($key) { case 'TRANSITION_BLINDS_HORIZONTAL': - $this->assertTrue($pres->elementExists($element.'/p:blinds[@dir=\'horz\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:blinds[@dir=\'horz\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_BLINDS_VERTICAL': - $this->assertTrue($pres->elementExists($element.'/p:blinds[@dir=\'vert\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:blinds[@dir=\'vert\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_CHECKER_HORIZONTAL': - $this->assertTrue($pres->elementExists($element.'/p:checker[@dir=\'horz\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:checker[@dir=\'horz\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_CHECKER_VERTICAL': - $this->assertTrue($pres->elementExists($element.'/p:checker[@dir=\'vert\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:checker[@dir=\'vert\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_CIRCLE_HORIZONTAL': - $this->assertTrue($pres->elementExists($element.'/p:circle[@dir=\'horz\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:circle[@dir=\'horz\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_CIRCLE_VERTICAL': - $this->assertTrue($pres->elementExists($element.'/p:circle[@dir=\'vert\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:circle[@dir=\'vert\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_COMB_HORIZONTAL': - $this->assertTrue($pres->elementExists($element.'/p:comb[@dir=\'horz\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:comb[@dir=\'horz\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_COMB_VERTICAL': - $this->assertTrue($pres->elementExists($element.'/p:comb[@dir=\'vert\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:comb[@dir=\'vert\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_COVER_DOWN': - $this->assertTrue($pres->elementExists($element.'/p:cover[@dir=\'d\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:cover[@dir=\'d\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_COVER_LEFT': - $this->assertTrue($pres->elementExists($element.'/p:cover[@dir=\'l\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:cover[@dir=\'l\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_COVER_LEFT_DOWN': - $this->assertTrue($pres->elementExists($element.'/p:cover[@dir=\'ld\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:cover[@dir=\'ld\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_COVER_LEFT_UP': - $this->assertTrue($pres->elementExists($element.'/p:cover[@dir=\'lu\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:cover[@dir=\'lu\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_COVER_RIGHT': - $this->assertTrue($pres->elementExists($element.'/p:cover[@dir=\'r\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:cover[@dir=\'r\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_COVER_RIGHT_DOWN': - $this->assertTrue($pres->elementExists($element.'/p:cover[@dir=\'rd\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:cover[@dir=\'rd\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_COVER_RIGHT_UP': - $this->assertTrue($pres->elementExists($element.'/p:cover[@dir=\'ru\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:cover[@dir=\'ru\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_COVER_UP': - $this->assertTrue($pres->elementExists($element.'/p:cover[@dir=\'u\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:cover[@dir=\'u\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_CUT': - $this->assertTrue($pres->elementExists($element.'/p:cut', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:cut', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_DIAMOND': - $this->assertTrue($pres->elementExists($element.'/p:diamond', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:diamond', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_DISSOLVE': - $this->assertTrue($pres->elementExists($element.'/p:dissolve', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:dissolve', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_FADE': - $this->assertTrue($pres->elementExists($element.'/p:fade', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:fade', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_NEWSFLASH': - $this->assertTrue($pres->elementExists($element.'/p:newsflash', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:newsflash', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_PLUS': - $this->assertTrue($pres->elementExists($element.'/p:plus', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:plus', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_PULL_DOWN': - $this->assertTrue($pres->elementExists($element.'/p:pull[@dir=\'d\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:pull[@dir=\'d\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_PULL_LEFT': - $this->assertTrue($pres->elementExists($element.'/p:pull[@dir=\'l\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:pull[@dir=\'l\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_PULL_RIGHT': - $this->assertTrue($pres->elementExists($element.'/p:pull[@dir=\'r\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:pull[@dir=\'r\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_PULL_UP': - $this->assertTrue($pres->elementExists($element.'/p:pull[@dir=\'u\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:pull[@dir=\'u\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_PUSH_DOWN': - $this->assertTrue($pres->elementExists($element.'/p:push[@dir=\'d\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:push[@dir=\'d\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_PUSH_LEFT': - $this->assertTrue($pres->elementExists($element.'/p:push[@dir=\'l\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:push[@dir=\'l\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_PUSH_RIGHT': - $this->assertTrue($pres->elementExists($element.'/p:push[@dir=\'r\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:push[@dir=\'r\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_PUSH_UP': - $this->assertTrue($pres->elementExists($element.'/p:push[@dir=\'u\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:push[@dir=\'u\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_RANDOM': - $this->assertTrue($pres->elementExists($element.'/p:random', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:random', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_RANDOMBAR_HORIZONTAL': - $this->assertTrue($pres->elementExists($element.'/p:randomBar[@dir=\'horz\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:randomBar[@dir=\'horz\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_RANDOMBAR_VERTICAL': - $this->assertTrue($pres->elementExists($element.'/p:randomBar[@dir=\'vert\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:randomBar[@dir=\'vert\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_SPLIT_IN_HORIZONTAL': - $this->assertTrue($pres->elementExists($element.'/p:split[@dir=\'in\'][@orient=\'horz\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:split[@dir=\'in\'][@orient=\'horz\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_SPLIT_OUT_HORIZONTAL': - $this->assertTrue($pres->elementExists($element.'/p:split[@dir=\'out\'][@orient=\'horz\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:split[@dir=\'out\'][@orient=\'horz\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_SPLIT_IN_VERTICAL': - $this->assertTrue($pres->elementExists($element.'/p:split[@dir=\'in\'][@orient=\'vert\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:split[@dir=\'in\'][@orient=\'vert\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_SPLIT_OUT_VERTICAL': - $this->assertTrue($pres->elementExists($element.'/p:split[@dir=\'out\'][@orient=\'vert\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:split[@dir=\'out\'][@orient=\'vert\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_STRIPS_LEFT_DOWN': - $this->assertTrue($pres->elementExists($element.'/p:strips[@dir=\'ld\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:strips[@dir=\'ld\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_STRIPS_LEFT_UP': - $this->assertTrue($pres->elementExists($element.'/p:strips[@dir=\'lu\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:strips[@dir=\'lu\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_STRIPS_RIGHT_DOWN': - $this->assertTrue($pres->elementExists($element.'/p:strips[@dir=\'rd\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:strips[@dir=\'rd\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_STRIPS_RIGHT_UP': - $this->assertTrue($pres->elementExists($element.'/p:strips[@dir=\'ru\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:strips[@dir=\'ru\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_WEDGE': - $this->assertTrue($pres->elementExists($element.'/p:wedge', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:wedge', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_WIPE_DOWN': - $this->assertTrue($pres->elementExists($element.'/p:wipe[@dir=\'d\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:wipe[@dir=\'d\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_WIPE_LEFT': - $this->assertTrue($pres->elementExists($element.'/p:wipe[@dir=\'l\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:wipe[@dir=\'l\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_WIPE_RIGHT': - $this->assertTrue($pres->elementExists($element.'/p:wipe[@dir=\'r\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:wipe[@dir=\'r\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_WIPE_UP': - $this->assertTrue($pres->elementExists($element.'/p:wipe[@dir=\'u\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:wipe[@dir=\'u\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_ZOOM_IN': - $this->assertTrue($pres->elementExists($element.'/p:zoom[@dir=\'in\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:zoom[@dir=\'in\']', 'ppt/slides/slide1.xml')); break; case 'TRANSITION_ZOOM_OUT': - $this->assertTrue($pres->elementExists($element.'/p:zoom[@dir=\'out\']', 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->elementExists($element . '/p:zoom[@dir=\'out\']', 'ppt/slides/slide1.xml')); break; } } @@ -910,4 +1056,23 @@ public function testTransition() $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); $this->assertContains('1', $pres->getElementAttribute($element, 'advClick', 'ppt/slides/slide1.xml')); } + + public function testVisibility() + { + $expectedElement = '/p:sld'; + + $oPhpPresentation = new PhpPresentation(); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($pres->elementExists($expectedElement, 'ppt/slides/slide1.xml')); + $this->assertFalse($pres->attributeElementExists($expectedElement, 'show', 'ppt/slides/slide1.xml')); + + $oSlide = $oPhpPresentation->getActiveSlide(); + $oSlide->setIsVisible(false); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($pres->elementExists($expectedElement, 'ppt/slides/slide1.xml')); + $this->assertTrue($pres->attributeElementExists($expectedElement, 'show', 'ppt/slides/slide1.xml')); + $this->assertEquals(0, $pres->getElementAttribute($expectedElement, 'show', 'ppt/slides/slide1.xml')); + } } diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptTablePropsTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptTablePropsTest.php new file mode 100644 index 000000000..e3ed1e3cd --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptTablePropsTest.php @@ -0,0 +1,34 @@ +assertTrue($oXMLDoc->fileExists('ppt/tableStyles.xml')); + $element = '/a:tblStyleLst'; + $this->assertTrue($oXMLDoc->elementExists($element, 'ppt/tableStyles.xml')); + $this->assertEquals('{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}', $oXMLDoc->getElementAttribute($element, 'def', 'ppt/tableStyles.xml')); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptViewPropsTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptViewPropsTest.php new file mode 100644 index 000000000..0ddba0b75 --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptViewPropsTest.php @@ -0,0 +1,64 @@ +assertTrue($oXMLDoc->fileExists('ppt/viewProps.xml')); + $this->assertTrue($oXMLDoc->elementExists($expectedElement, 'ppt/viewProps.xml')); + $this->assertEquals('0', $oXMLDoc->getElementAttribute($expectedElement, 'showComments', 'ppt/viewProps.xml')); + $this->assertEquals(PresentationProperties::VIEW_SLIDE, $oXMLDoc->getElementAttribute($expectedElement, 'lastView', 'ppt/viewProps.xml')); + } + + public function testCommentVisible() + { + $expectedElement ='/p:viewPr'; + + $oPhpPresentation = new PhpPresentation(); + $oPhpPresentation->getPresentationProperties()->setCommentVisible(true); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('ppt/viewProps.xml')); + $this->assertTrue($oXMLDoc->elementExists($expectedElement, 'ppt/viewProps.xml')); + $this->assertEquals(1, $oXMLDoc->getElementAttribute($expectedElement, 'showComments', 'ppt/viewProps.xml')); + } + + public function testLastView() + { + $expectedElement ='/p:viewPr'; + $expectedLastView = PresentationProperties::VIEW_OUTLINE; + + $oPhpPresentation = new PhpPresentation(); + $oPhpPresentation->getPresentationProperties()->setLastView($expectedLastView); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('ppt/viewProps.xml')); + $this->assertTrue($oXMLDoc->elementExists($expectedElement, 'ppt/viewProps.xml')); + $this->assertEquals($expectedLastView, $oXMLDoc->getElementAttribute($expectedElement, 'lastView', 'ppt/viewProps.xml')); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/RelationshipsTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/RelationshipsTest.php new file mode 100644 index 000000000..7c7ef5631 --- /dev/null +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/RelationshipsTest.php @@ -0,0 +1,22 @@ +setAuthor($oAuthor); + $oPhpPresentation = new PhpPresentation(); + $oPhpPresentation->getActiveSlide()->addShape($oComment); + + $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($pres->elementExists('/Relationships/Relationship[@Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/commentAuthors"]', 'ppt/_rels/presentation.xml.rels')); + } +} diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007Test.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007Test.php index 9036a0ba0..6ea379f99 100644 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007Test.php +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007Test.php @@ -34,16 +34,6 @@ class PowerPoint2007Test extends \PHPUnit_Framework_TestCase public function testConstruct() { $objectPrefix = 'PhpOffice\\PhpPresentation\\Writer\\PowerPoint2007\\'; - $parts = array( - 'contenttypes' => 'ContentTypes', - 'docprops' => 'DocProps', - 'rels' => 'Rels', - 'theme' => 'Theme', - 'presentation' => 'Presentation', - 'slide' => 'Slide', - 'drawing' => 'Drawing', - 'chart' => 'Chart', - ); $oPhpPresentation = new PhpPresentation(); $object = new PowerPoint2007($oPhpPresentation); @@ -51,10 +41,6 @@ public function testConstruct() $this->assertInstanceOf('PhpOffice\\PhpPresentation\\PhpPresentation', $object->getPhpPresentation()); $this->assertEquals('./', $object->getDiskCachingDirectory()); $this->assertInstanceOf("{$objectPrefix}LayoutPack\\PackDefault", $object->getLayoutPack()); - foreach ($parts as $partName => $objectName) { - $this->assertInstanceOf($objectPrefix . $objectName, $object->getWriterPart($partName)); - } - $this->assertInstanceOf('PhpOffice\\PhpPresentation\\HashTable', $object->getDrawingHashTable()); } /** @@ -63,15 +49,8 @@ public function testConstruct() public function testSave() { $filename = tempnam(sys_get_temp_dir(), 'PhpPresentation'); - $imageFile = PHPPRESENTATION_TESTS_BASE_DIR . '/resources/images/PhpPresentationLogo.png'; $oPhpPresentation = new PhpPresentation(); - $slide = $oPhpPresentation->getActiveSlide(); - $slide->createRichTextShape(); - $slide->createLineShape(10, 10, 10, 10); - $slide->createChartShape()->getPlotArea()->setType(new \PhpOffice\PhpPresentation\Shape\Chart\Type\Bar3D()); - $slide->createDrawingShape()->setName('Drawing')->setPath($imageFile); - $slide->createTableShape()->createRow(); $object = new PowerPoint2007($oPhpPresentation); $object->save($filename); @@ -82,47 +61,55 @@ public function testSave() } /** - * Test get writer part null + * Test save with empty filename + * + * @expectedException Exception + * @expectedExceptionMessage Filename is empty */ - public function testGetWriterPartNull() + public function testSaveEmptyException() { - $object = new PowerPoint2007(new PhpPresentation()); + $oPhpPresentation = new PhpPresentation(); - $this->assertNull($object->getWriterPart('foo')); + $object = new PowerPoint2007($oPhpPresentation); + $object->save(''); } /** - * Test get PhpPresentation exception + * Test save with empty assignation * * @expectedException Exception * @expectedExceptionMessage No PhpPresentation assigned. */ - public function testGetPhpPresentationException() + public function testSaveUnassignedException() { $object = new PowerPoint2007(); - $object->getPhpPresentation(); + $object->save('filename.pptx'); } /** - * Test set/get Office 2003 compatibility + * Test get PhpPresentation exception + * + * @expectedException Exception + * @expectedExceptionMessage No PhpPresentation assigned. */ - public function testSetHasOffice2003Compatibility() + public function testGetPhpPresentationException() { - $object = new PowerPoint2007(new PhpPresentation()); - $this->assertFalse($object->hasOffice2003Compatibility()); - - $object->setOffice2003Compatibility(true); - $this->assertTrue($object->hasOffice2003Compatibility()); + $object = new PowerPoint2007(); + $object->getPhpPresentation(); } /** - * Test set/get disk caching + * Test disk caching */ - public function testSetGetUseDiskCaching() + public function testDiskCaching() { $object = new PowerPoint2007(new PhpPresentation()); $this->assertFalse($object->hasDiskCaching()); + $object->setUseDiskCaching(true); + $this->assertTrue($object->hasDiskCaching()); + $this->assertEquals('./', $object->getDiskCachingDirectory()); + $object->setUseDiskCaching(true, sys_get_temp_dir()); $this->assertTrue($object->hasDiskCaching()); $this->assertEquals(sys_get_temp_dir(), $object->getDiskCachingDirectory()); @@ -132,36 +119,29 @@ public function testSetGetUseDiskCaching() * Test set/get disk caching exception * * @expectedException Exception + * @expectedExceptionMessage Directory does not exist: foo */ - public function testSetUseDiskCachingException() + public function testCachingException() { $object = new PowerPoint2007(new PhpPresentation()); $object->setUseDiskCaching(true, 'foo'); } - public function testMarkAsFinal() + /** + * Test LayoutPack + * @deprecated 0.7 + */ + public function testLayoutPack() { - $oPhpPresentation = new PhpPresentation(); - - $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - $this->assertFalse($pres->elementExists('/Properties/property[@name="_MarkAsFinal"]', 'docProps/custom.xml')); - $this->assertFalse($pres->elementExists('/cp:coreProperties/cp:contentStatus', 'docProps/core.xml')); - - $oPhpPresentation->markAsFinal(true); - - $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - $this->assertTrue($pres->elementExists('/Properties', 'docProps/custom.xml')); - $this->assertTrue($pres->elementExists('/Properties/property', 'docProps/custom.xml')); - $this->assertTrue($pres->elementExists('/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="_MarkAsFinal"]', 'docProps/custom.xml')); - $this->assertTrue($pres->elementExists('/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="_MarkAsFinal"]/vt:bool', 'docProps/custom.xml')); - $this->assertTrue($pres->elementExists('/cp:coreProperties/cp:contentStatus', 'docProps/core.xml')); - $this->assertEquals('Final', $pres->getElement('/cp:coreProperties/cp:contentStatus', 'docProps/core.xml')->nodeValue); + $oLayoutPack = $this->getMock('PhpOffice\\PhpPresentation\\Writer\\PowerPoint2007\\LayoutPack\\AbstractLayoutPack'); - $oPhpPresentation->markAsFinal(false); + $object = new PowerPoint2007(); - $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); - $this->assertFalse($pres->elementExists('/Properties/property[@name="_MarkAsFinal"]', 'docProps/custom.xml')); - $this->assertFalse($pres->elementExists('/cp:coreProperties/cp:contentStatus', 'docProps/core.xml')); + $this->assertInstanceOf("PhpOffice\\PhpPresentation\\Writer\\PowerPoint2007\\LayoutPack\\AbstractLayoutPack", $object->getLayoutPack()); + $this->assertInstanceOf("PhpOffice\\PhpPresentation\\Writer\\PowerPoint2007", $object->setLayoutPack()); + $this->assertNull($object->getLayoutPack()); + $this->assertInstanceOf("PhpOffice\\PhpPresentation\\Writer\\PowerPoint2007", $object->setLayoutPack($oLayoutPack)); + $this->assertInstanceOf("PhpOffice\\PhpPresentation\\Writer\\PowerPoint2007\\LayoutPack\\AbstractLayoutPack", $object->getLayoutPack()); } public function testZoom() @@ -177,7 +157,7 @@ public function testZoom() $this->assertEquals('100', $pres->getElementAttribute('/p:viewPr/p:slideViewPr/p:cSldViewPr/p:cViewPr/p:scale/a:sy', 'd', 'ppt/viewProps.xml')); $value = rand(1, 100); - $oPhpPresentation->setZoom($value); + $oPhpPresentation->getPresentationProperties()->setZoom($value); $pres = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); $this->assertTrue($pres->elementExists('/p:viewPr/p:slideViewPr/p:cSldViewPr/p:cViewPr/p:scale/a:sx', 'ppt/viewProps.xml')); @@ -187,4 +167,21 @@ public function testZoom() $this->assertEquals($value * 100, $pres->getElementAttribute('/p:viewPr/p:slideViewPr/p:cSldViewPr/p:cViewPr/p:scale/a:sy', 'n', 'ppt/viewProps.xml')); $this->assertEquals('100', $pres->getElementAttribute('/p:viewPr/p:slideViewPr/p:cSldViewPr/p:cViewPr/p:scale/a:sy', 'd', 'ppt/viewProps.xml')); } + + public function testFeatureThumbnail() + { + $imagePath = PHPPRESENTATION_TESTS_BASE_DIR.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.'PhpPresentationLogo.png'; + + $xPathManifest = '/Relationships/Relationship[@Target=\'docProps/thumbnail.jpeg\'][@Type=\'http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail\']'; + $oPhpPresentation = new PhpPresentation(); + + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('_rels/.rels')); + $this->assertFalse($oXMLDoc->elementExists($xPathManifest, '_rels/.rels')); + + $oPhpPresentation->getPresentationProperties()->setThumbnailPath($imagePath); + $oXMLDoc = TestHelperDOCX::getDocument($oPhpPresentation, 'PowerPoint2007'); + $this->assertTrue($oXMLDoc->fileExists('_rels/.rels')); + $this->assertTrue($oXMLDoc->elementExists($xPathManifest, '_rels/.rels')); + } } diff --git a/tests/PhpPresentation/Tests/Writer/SerializedTest.php b/tests/PhpPresentation/Tests/Writer/SerializedTest.php index 1a50426cb..3e3d084c4 100644 --- a/tests/PhpPresentation/Tests/Writer/SerializedTest.php +++ b/tests/PhpPresentation/Tests/Writer/SerializedTest.php @@ -55,7 +55,7 @@ public function testSaveEmpty() /** * @expectedException \Exception - * @expectedExceptionMessage PhpPresentation object unassigned. + * @expectedExceptionMessage No PhpPresentation assigned. */ public function testSaveNoObject() { diff --git a/tests/PhpPresentation/Tests/_includes/TestHelperDOCX.php b/tests/PhpPresentation/Tests/_includes/TestHelperDOCX.php index 0910488c2..6970ee75e 100644 --- a/tests/PhpPresentation/Tests/_includes/TestHelperDOCX.php +++ b/tests/PhpPresentation/Tests/_includes/TestHelperDOCX.php @@ -35,11 +35,11 @@ class TestHelperDOCX /** * Get document content * - * @param \PhpOffice\PhpPresentation\PhpPresentation $PhpPresentation + * @param \PhpOffice\PhpPresentation\PhpPresentation $phpPresentation * @param string $writerName * @return \PhpOffice\PhpPresentation\Tests\XmlDocument */ - public static function getDocument(PhpPresentation $phpPresentation, $writerName = 'PowerPoint2007') + public static function getDocument(PhpPresentation $phpPresentation, $writerName) { self::$file = tempnam(sys_get_temp_dir(), 'PhpPresentation'); if (!is_dir(sys_get_temp_dir() . '/PhpPresentation_Unit_Test/')) { diff --git a/tests/PhpPresentation/Tests/_includes/XmlDocument.php b/tests/PhpPresentation/Tests/_includes/XmlDocument.php index 57075ee30..91dbd15fe 100644 --- a/tests/PhpPresentation/Tests/_includes/XmlDocument.php +++ b/tests/PhpPresentation/Tests/_includes/XmlDocument.php @@ -79,14 +79,27 @@ public function getFileDom($file = 'word/document.xml') $file = $this->path . '/' . $file; $this->dom = new \DOMDocument(); $strContent = file_get_contents($file); + // docProps/app.xml + if ($baseFile == 'docProps/app.xml') { + $strContent = str_replace(' xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"', '', $strContent); + } // docProps/custom.xml if ($baseFile == 'docProps/custom.xml') { $strContent = str_replace(' xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"', '', $strContent); } + // _rels/.rels + if (strpos($baseFile, '_rels/') !== false && strpos($baseFile, '.rels') !== false) { + $strContent = str_replace(' xmlns="http://schemas.openxmlformats.org/package/2006/relationships"', '', $strContent); + } $this->dom->loadXML($strContent); return $this->dom; } + public function fileExists($file) + { + return file_exists($this->path . '/' . $file); + } + /** * Get node list * @@ -178,4 +191,17 @@ public function elementExists($path, $file = 'word/document.xml') $nodeList = $this->getNodeList($path, $file); return !($nodeList->length == 0); } + + /** + * Check if element exists + * + * @param string $path + * @param string $file + * @return string + */ + public function elementCount($path, $file) + { + $nodeList = $this->getNodeList($path, $file); + return $nodeList->length; + } } diff --git a/tests/resources/files/Issue_00150.pptx b/tests/resources/files/Issue_00150.pptx new file mode 100644 index 000000000..641af8d80 Binary files /dev/null and b/tests/resources/files/Issue_00150.pptx differ diff --git a/tests/resources/videos/sintel_trailer-480p.ogv b/tests/resources/videos/sintel_trailer-480p.ogv new file mode 100644 index 000000000..2f37321f7 Binary files /dev/null and b/tests/resources/videos/sintel_trailer-480p.ogv differ