PHPOffice / PHPPresentation

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

Notes crash PPTX unless they're on each slide #83

Open JewrassicPark opened 9 years ago

JewrassicPark commented 9 years ago

Basic method is

$pp = new PhpPowerpoint();
//don't put any notes on the first slide 
$slide = $pp->createSlide();
$note = $slide->getNote();
$text = $note->createRichTextShape()->setHeight(300)->setWidth(600);
$text->createTextRun("Any Note");
$oWriterPPTX = IOFactory::createWriter($pp, 'PowerPoint2007');
$oWriterPPTX->save($savePath);      

This causes Office PowerPoint to crash.

It can be worked around by adding a non empty note to every slide.

e.g. this works:

$pp = new PhpPowerpoint();
//putting a note on the first slide
$slide  = $pp->getActiveSlide();
$note = $slide->getNote();
$text = $note->createRichTextShape()->setHeight(300)->setWidth(600);
$text->createTextRun("Any Note");
//putting a note on the second slide 
$slide = $pp->createSlide();
$note = $slide->getNote();
$text = $note->createRichTextShape()->setHeight(300)->setWidth(600);
$text->createTextRun("Any Note");
$oWriterPPTX = IOFactory::createWriter($pp, 'PowerPoint2007');
$oWriterPPTX->save($savePath);      

Notes generated with PHPPowerpoint show up in the pptx zip according to slide

noteSlide2.xml => slide # 2, noteSlideX.xml => slide # x

When created with MS Office, the noteSlides are just numbered sequentially

noteSlide1.xml => corresponds to some slide not necessarily #1 , noteSlide2.xml => same thing

They also have a _rels relationship folder and a noteSlideMaster folder. I couldn't work out the exact mapping or how to avoid this crashing issues. Currently I'm using the workaround.

JewrassicPark commented 9 years ago

Another notes bug: Inconsistently when generating large reports, a notesslide relationship in the slide rels dir file for a slide will have the wrong slide number (which currently has to be the same as the slide because every slide needs notes if one slide has notes). This causes office to show the repair dialogue. This is extremely inconsistent. I'll generate the same powerpoint twice and might receive this error on one.

Progi1984 commented 9 years ago

@JewrassicPark I generate a file with PHPPowerPoint (the last version of the develop branch). I open the generated file with MS Office PowerPoint Viewer : No problem. I validate the generated file with Open XML SDK 2.5 Productivity Tool for Microsoft Office : No problem.

JewrassicPark commented 9 years ago

I still get the same initial bug with latest dev branch

It doesn't crash if notes are on every slide:

    $p = new Presentation();
    $p->debug = true;
    $p->addSlide(); 
    $p->addNote(ARRAY('t'=>'testing'));
    $p->addSlide(); 
    $p->addNote(ARRAY('t'=>'testing'));
    $file = $p->save();

But it crashes consistently immediately upon opening in PowerPoint 2013 if one slide has notes and the other doesnt

    $p = new Presentation();
    $p->debug = true;
    $p->addSlide(); 
    //$p->addNote(ARRAY('t'=>'testing'));
    $p->addSlide(); 
    $p->addNote(ARRAY('t'=>'testing'));
    $file = $p->save();

The other bug with the numbering was only observed when generating large powerpoints (50+ slides) and it wasn't consistent (generated same powerpoint twice, one needed to be repaired and one didn't)

Progi1984 commented 9 years ago

@JewrassicPark Firstly, I try to reproduce your first bug (before bug of numerotation); But your code use home made methods, I suppose. So I use this code

$pp = new PhpPowerpoint();
$slide = $pp->createSlide();

$slide = $pp->createSlide();
$note = $slide->getNote();
$text = $note->createRichTextShape()->setHeight(300)->setWidth(600);
$text->createTextRun("Any Note");

$oWriterPPTX = IOFactory::createWriter($pp, 'PowerPoint2007');
$oWriterPPTX->save('#83/Sample83.pptx');

Always ok with the PPTX Viewer, the SDK Validator and LibreOffice. Without reproducing this bug, I can't fix it.

JewrassicPark commented 9 years ago

That code exactly immediately crashes my Powerpoint 2013.

To not crash it, I have to add a note to the first slide you create and the first slide created automatically by the constructor

    $pp = new PhpPowerpoint();
    $slide = $pp->getActiveSlide();
    $note = $slide->getNote();
    $text = $note->createRichTextShape()->setHeight(300)->setWidth(600);
    $text->createTextRun("Any Note");

    $slide = $pp->createSlide();
    $note = $slide->getNote();
    $text = $note->createRichTextShape()->setHeight(300)->setWidth(600);
    $text->createTextRun("Any Note");

    $slide = $pp->createSlide();
    $note = $slide->getNote();
    $text = $note->createRichTextShape()->setHeight(300)->setWidth(600);
    $text->createTextRun("Any Note");

    $oWriterPPTX = IOFactory::createWriter($pp, 'PowerPoint2007');
    $oWriterPPTX->save("/$path/test-ppt.pptx");
Progi1984 commented 9 years ago

@JewrassicPark No problem with the SDK Validator and LibreOffice... :S

Have you a sample file generated with Powerpoint 2013 with a simple note for looking differences ?

Progi1984 commented 9 years ago

@JewrassicPark I just tried on a PPT2007 and no problem with your code and the generated file.

Progi1984 commented 9 years ago

@JewrassicPark PHPPowerPoint 0.4.0 has been released. I would like to solve your issue for 0.5.0. Could you answer to my question ?

JewrassicPark commented 9 years ago

Sorry my job doesn't afford me a lot of chances to work on the phpowerpoint code we use. I just ran two tests. Both with two slides. The first had notes only on the second slide, and that worked which goes against my previous findings. The second one had notes only on the first slide, this caused the powerpoint to either need to be repaired or just froze and crashed. Using office 2013.

Progi1984 commented 9 years ago

@JewrassicPark Thanks for your feedback

So this code could crash Office 2013 :

   $pp = new PhpPowerpoint();
    $slide = $pp->getActiveSlide();
    $note = $slide->getNote();
    $text = $note->createRichTextShape()->setHeight(300)->setWidth(600);
    $text->createTextRun("Any Note");

    $slide = $pp->createSlide();
    $note = $slide->getNote();
    $text = $note->createRichTextShape()->setHeight(300)->setWidth(600);
    $text->createTextRun("Any Note");

    $slide = $pp->createSlide();

    $oWriterPPTX = IOFactory::createWriter($pp, 'PowerPoint2007');
    $oWriterPPTX->save("/$path/test-ppt.pptx");

The generated file : https://www.dropbox.com/s/28sipncx8vr2e6k/83.pptx?dl=0

Progi1984 commented 8 years ago

@JewrassicPark PHPPowerPoint 0.6.0 has been released. I would like to solve your issue for 0.7.0. Could you answer to my question ?

JewrassicPark commented 8 years ago

@Progi1984 Sorry, I've had a request for this feature at work again and I'll revisit this issue shortly.

JewrassicPark commented 8 years ago

I can consistently get it to crash if there is no notes on the first slide but there are notes on any of the next slides. Using 0.6.0 with Powerpoint 2013 on Windows. I understand this is contrary to the previous tests. My current workaround is to force a slide to have blank notes by default. To be clear the powerpoints are generated via PHP on a CENTOS server but I'm opening them on Windows.

JewrassicPark commented 8 years ago

I'm getting reports from my users that powerpoints with notes enabled are breaking more often when generating. I can't say for sure that it's this feature but it's really the only major change I've made to my codebase in awhile. I think there's got to be something implemented incorrectly with notes still.

Progi1984 commented 8 years ago

@JewrassicPark : So this code should be generating a crashed file ?

  $pp = new PhpPowerpoint();
  $slide1= $pp->getActiveSlide();
  // With no notes

  $slide = $pp->createSlide();
  $note = $slide->getNote();
  $text = $note->createRichTextShape()->setHeight(300)->setWidth(600);
  $text->createTextRun("Any Note");

  $slide = $pp->createSlide();

  $oWriterPPTX = IOFactory::createWriter($pp, 'PowerPoint2007');
  $oWriterPPTX->save("/$path/test-ppt.pptx");
JewrassicPark commented 8 years ago

Yes I believe so, that was causing consistent crashing on open for me. If I use my workaround to put a dummy note on every slide, it still seems to be causing sporadic issues.

Progi1984 commented 8 years ago

@JewrassicPark Could you check this bug with the last version of the develop branch ?

JewrassicPark commented 8 years ago

Yeah it's still present.

I tried a few combinations involving three slides.

slide 1 = s1, slide2 = s2, slide3 = s3

s1 with notes, s2 with notes, s2 no notes = Works s1 with notes, s2 no notes, s3 with notes = Works s1 with no notes, s2 with notes, s3 with notes = Crashes on opening ppt s1 with no notes, s2 with notes, s3 no notes = Crashes on opening ppt

Even when I was forcing every slide to have notes by default (a workaround), I was getting user reports of slides being broken. Perhaps it's some kind of indexing issue.

Progi1984 commented 8 years ago

@JewrassicPark Shit, i try with a sample code :

// 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->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;
$oSlide1 = $objPHPPresentation->getActiveSlide();
echo date('H:i:s') . ' Create slide' . EOL;
$oSlide2 = $objPHPPresentation->createSlide();
echo date('H:i:s') . ' Create slide' . EOL;
$oSlide3 = $objPHPPresentation->createSlide();

// Set Note
echo date('H:i:s') . ' Set Note' . EOL;
$oNote1 = $oSlide2->getNote();

$oRichText = $oNote1->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');
$oRichText->createParagraph()->createTextRun('Supports writing to different file formats');

And no problem with PowerPoint : screen shot 05-21-16 at 03 28 pm

May be it's a problem of version, etc...

Could you send me a sample of PPTX which crashes and its code ?

JewrassicPark commented 8 years ago

I used the exact sample code you have above with the latest dev branch

http://www.filedropper.com/crashesonstartpowerpoint2013

it hard crashes immediately for me on file open consistently.

Powerpoint 2013 on Windows 10

Progi1984 commented 8 years ago

@JewrassicPark : Just for keeping the file on Github : crashesOnStartPowerpoint2013.pptx

No problem with PowerPoint 2007. And it's valid againt MS Office 2013 format : screen shot 05-31-16 at 10 56 pm

JewrassicPark commented 8 years ago

Perhaps you can send me a file generated with the same code and I can confirm it's not an issue with my servers

davemulder commented 8 years ago

@Progi1984 — I've been tracking down a similar issue as @JewrassicPark, except with Mac Keynote. The presence of any programmatically generated slide notes appears to create a PPTX that Keynote, at least version 6.6.2, does not want to open (produces a generic error). This was also true using the code and file you provided in this thread.

I know that your work is geared around PowerPoint, specifically, but I wanted to give you a heads up as it could be related to the PowerPoint crashing issue being discussed here.

davemulder commented 7 years ago

Tested with PHPPresentation 0.7 and what I noted in my previous comment is still an issue with Keynote. Any programmatically created notes seem to generate a PPTX file that cannot be opened in Keynote.

Based on thorough inspection of the PHPPresentation-generated PPTX, it seems that it's missing "notesMasters" despite having slide notes present. The PPTX still validates (using the Open XML validation tool) but evidently that's not good enough for Keynote.

NigelSwinson commented 7 months ago

FYI, testing with the develop branch, on Windows, and this issue remains:

<?php

include_once 'Sample_Header.php';

use PhpOffice\PhpPresentation\PhpPresentation;
use PhpOffice\PhpPresentation\Style\Alignment;
use PhpOffice\PhpPresentation\Style\Color;
$objPHPPresentation = new PhpPresentation();

$currentSlide = $objPHPPresentation->getActiveSlide();
$oNote = $currentSlide->getNote();
// ### If you don't include this line, it will crash
//$oRichText = $oNote->createRichTextShape();

// Create second slide with a note
$currentSlide = $objPHPPresentation->createSlide();
$oNote = $currentSlide->getNote();
$oRichText = $oNote->createRichTextShape();
$oRichText->createTextRun('Second note');

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

I haven't tried the pull request, but will do so now and see if that addresses the issue.

NigelSwinson commented 7 months ago

A fix: in Writer/PowerPoint2007.php add this method. It will scan the presentation seeing if any note exists, and if it does, it will add a blank RichTextShape note to the slide preventing the crash.

// ### Add a using statement so we can reference the Note class
use PhpOffice\PhpPresentation\Slide\Note;

....

    // ### A new private member of the PowerPoint2007 class
    private function insertNecessaryNotes() {
        $oPresentation = $this->getPhpPresentation();
        // Work out if we need to write any notes
        // ### https://github.com/PHPOffice/PHPPresentation/issues/83 If we only write out notes files for the slides that have notes, then Powerpoint will crash.
        // . e.g. if your slide 5 is the first slide to have a note, meaning you will write out slide5.xml, but didn't write out slide1.xml-slide4.xml then Powerpoint
        // will crash. To fix, we first scan through the deck to see if anyone wants to write any notes, and anyone does, we make sure all slides have an empty note.
        $bMustWriteNotes = false;
        foreach ($oPresentation->getAllSlides() as $idx => $oSlide) {
            $oNote = $oSlide->GetNote();
            if (!($oNote instanceof Note)) continue;
            if (count($oSlide->getNote()->getShapeCollection()) == 0) continue;
            // Stop if we find any slide which has a note.
            $bMustWriteNotes = true;
            break;
        }

        // If we must write notes, then make sure all slides have notes.
        if ($bMustWriteNotes) {
            foreach ($oPresentation->getAllSlides() as $idx => $oSlide) {
                if (!($oSlide->getNote() instanceof Note)) continue;
                $oNote = $oSlide->getNote();
                if (count($oNote->getShapeCollection()) > 0) continue;
                // If we don't provoke the need for a notes slide, then this powerpoint will crash
                $oNote->createRichTextShape();
            }
        }
    }

Then call it in save() just before the foreach loop().

        ksort($arrayFiles);

        // ### Call here, before we start writing any content.
        $this->insertNecessaryNotes();

        foreach ($arrayFiles as $o) {
            $oService = $o->newInstance();
            $oService->setZip($oZip);
            $oService->setPresentation($oPresentation);
            $oService->setDrawingHashTable($this->getDrawingHashTable());
            $oZip = $oService->render();
            unset($oService);
        }