PHPOffice / PHPPresentation

A pure PHP library for reading and writing presentations documents
https://phpoffice.github.io/PHPPresentation
Other
1.3k stars 519 forks source link

Powerpoint2007 Reader : Implement fill in RichText #511

Open TrevorBradleyCDN opened 5 years ago

TrevorBradleyCDN commented 5 years ago

I'm having an issue where I'm attempting to load an existing PPTX file, with a background fill colour, defined as an "Area" in LibreOffice Impress, or as a solidFill in the slide XML.

All background fill colours, or "Area" shapes, appear to be completely discarded by IOFactory's load function.

I thought it was just me, so I modified Sample_06_Fill.php to attempt to reload, then resave the PPTX file it just generated, adding the following block of code just before the Sample_Footer:

$oReaderPPTX = IOFactory::createReader('PowerPoint2007'); $phpPowerpoint = $oReaderPPTX->load('results/Sample_06_Fill.pptx'); $oWriterPPTX = IOFactory::createWriter($phpPowerpoint, 'PowerPoint2007'); $oWriterPPTX->save("results/Sample_06_Fill_Reload.pptx");

The resultant PPTX file has all its background colours stripped out.

I've done tests with very simple PPTX files, where there's a single area of colour on the page and nothing else, and the slide's shape collection is entirely empty.

Does the load function discard areas?

Sample_06_Fill_Reload.php.txt

<?php

include_once 'Sample_Header.php';

use PhpOffice\PhpPresentation\PhpPresentation;
use PhpOffice\PhpPresentation\Style\Alignment;
use PhpOffice\PhpPresentation\Style\Color;
use PhpOffice\PhpPresentation\Style\Fill;
use PhpOffice\PhpPresentation\IOFactory;

// Create new PHPPresentation object
echo date('H:i:s') . ' Create new PHPPresentation object' . EOL;
$objPHPPresentation = new PhpPresentation();

// Set properties
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;
$currentSlide = $objPHPPresentation->getActiveSlide();

for($inc = 1 ; $inc <= 4 ; $inc++){
    // Create a shape (text)
    echo date('H:i:s') . ' Create a shape (rich text)'.EOL;
    $shape = $currentSlide->createRichTextShape()
                          ->setHeight(200)
                          ->setWidth(300);
    if($inc == 1 || $inc == 3){
        $shape->setOffsetX(10);
    } else {
        $shape->setOffsetX(320);
    }
    if($inc == 1 || $inc == 2){
        $shape->setOffsetY(10);
    } else {
        $shape->setOffsetY(220);
    }
    $shape->getActiveParagraph()->getAlignment()->setHorizontal( Alignment::HORIZONTAL_CENTER );

    switch ($inc) {
        case 1 :
            $shape->getFill()->setFillType(Fill::FILL_NONE);
            break;
        case 2 :
            $shape->getFill()->setFillType(Fill::FILL_GRADIENT_LINEAR)->setRotation(90)->setStartColor(new Color( 'FF4672A8' ))->setEndColor(new Color( 'FF000000' ));
            break;
        case 3 :
            $shape->getFill()->setFillType(Fill::FILL_GRADIENT_PATH)->setRotation(90)->setStartColor(new Color( 'FF4672A8' ))->setEndColor(new Color( 'FF000000' ));
            break;
        case 4 :
            $shape->getFill()->setFillType(Fill::FILL_SOLID)->setRotation(90)->setStartColor(new Color( 'FF4672A8' ))->setEndColor(new Color( 'FF4672A8' ));
            break;
    }

    $textRun = $shape->createTextRun('Use PHPPresentation!');
    $textRun->getFont()->setBold(true)
                       ->setSize(30)
                       ->setColor( new Color('FFE06B20') );
}

$oReaderPPTX = IOFactory::createReader('PowerPoint2007');
$phpPowerpoint = $oReaderPPTX->load('results/Sample_06_Fill.pptx');
$oWriterPPTX = IOFactory::createWriter($phpPowerpoint, 'PowerPoint2007');
$oWriterPPTX->save("results/Sample_06_Fill_Reload.pptx");

// Save file
echo write($objPHPPresentation, basename(__FILE__, '.php'), $writers);
if (!CLI) {
    include_once 'Sample_Footer.php';
}

Sample_06_Fill.pptx

Sample_06_Fill_Reload.pptx

SampleArea.pptx

SampleArea's Slide XML:

`

`
Progi1984 commented 5 years ago

It's not implemented for the moment :).

TrevorBradleyCDN commented 5 years ago

Here's hoping for Hacktoberfest then. :)

TrevorBradleyCDN commented 5 years ago

I wasn't patient enough for Hacktoberfest. :)

Note that this is NOT a complete solution. It only takes into account solid backgrounds. But it might be a strong hint to anyone who wishes to tackle this.

I subclassed Reader/PowerPoint2007.php for my own purposes. Here's my revised loadShapeRichText(). Note that the initial return statement has been dropped, that a final else statement has been handled to add a paragraph in case one has been missing, and a new $oElement block that watches for a p:spPr/a:solidFill/a:srgbClr block.

  protected function loadShapeRichText(XMLReader $document, \DOMElement $node, $oSlide)
  {
    // Core
    $oShape = $oSlide->createRichTextShape();

    $oShape->setParagraphs(array());
    // Variables
    if ($oSlide instanceof AbstractSlide) {
      $this->fileRels = $oSlide->getRelsIndex();
    }

    $oElement = $document->getElement('p:spPr/a:xfrm', $node);
    if ($oElement instanceof \DOMElement && $oElement->hasAttribute('rot')) {
      $oShape->setRotation(CommonDrawing::angleToDegrees($oElement->getAttribute('rot')));
    }

    $oElement = $document->getElement('p:spPr/a:xfrm/a:off', $node);
    if ($oElement instanceof \DOMElement) {
      if ($oElement->hasAttribute('x')) {
        $oShape->setOffsetX(CommonDrawing::emuToPixels($oElement->getAttribute('x')));
      }
      if ($oElement->hasAttribute('y')) {
        $oShape->setOffsetY(CommonDrawing::emuToPixels($oElement->getAttribute('y')));
      }
    }

    $oElement = $document->getElement('p:spPr/a:xfrm/a:ext', $node);
    if ($oElement instanceof \DOMElement) {
      if ($oElement->hasAttribute('cx')) {
        $oShape->setWidth(CommonDrawing::emuToPixels($oElement->getAttribute('cx')));
      }
      if ($oElement->hasAttribute('cy')) {
        $oShape->setHeight(CommonDrawing::emuToPixels($oElement->getAttribute('cy')));
      }
    }

    $oElement = $document->getElement('p:spPr/a:solidFill/a:srgbClr', $node);
    if (is_object($oElement) && $oElement->hasAttribute('val')) {
      $oColor = new Color();
      $oColor->setRGB($oElement->getAttribute('val'));
      $oShape->getFill()->setFillType(Fill::FILL_SOLID)
        ->setStartColor($oColor)
        ->setEndColor($oColor);
    }

    $oElement = $document->getElement('p:nvSpPr/p:nvPr/p:ph', $node);
    if ($oElement instanceof \DOMElement) {
      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) {
      $this->loadParagraph($document, $oElement, $oShape);
    }

    if (count($oShape->getParagraphs()) > 0) {
      $oShape->setActiveParagraph(0);
    } else {
      // Create missing shape
      $oShape->createParagraph();
    }
  }