How to create a Word file with php in Symfony 3

How to create a Word file with php in Symfony 3

Many PHP applications require to export documents in Microsoft Word formats, this task can be easily achieved using PHPWord. PHPWord is a library written in pure PHP that provides a set of classes to write to and read from different document file formats. The current version of PHPWord supports Microsoft Office Open XML (OOXML or OpenXML), OASIS Open Document Format for Office Applications (OpenDocument or ODF), Rich Text Format (RTF), HTML, and PDF. PHPWord is an open source project licensed under the terms of LGPL version 3. PHPWord is aimed to be a high quality software product by incorporating continuous integration and unit testing. You can learn more about PHPWord by reading the Developers' Documentation and the API Documentation.

With PHPWord you can create beautiful Word and PDF documents from scratch. Create templates and edit just some variables to make the creation of word documents easier. In this article, you will learn how to implement PHPWord in a Symfony 3 project.

Requirements

In order to create word files with PHP , as previously mentioned, we are going to depend on the PHPWord library. To install this library as a dependency in your Symfony project, execute the following command in a terminal:

composer install phpoffice/phpword

Or modify the composer.json file of your project and add the library manually and then execute composer install:

{
    "require": {
       "phpoffice/phpword": "v0.13.*"
    }
}

After the download, your project should have PhpWord available for its usage. If you want more information about this library, please visit the official Github repository.

Examples

In this article, we are going to cover the 4 most used features by the developers when working with PHPWord. If you need more examples, we recommend you to check all the examples of PHPWord here (although they're not Symfony based, i'm sure you will understand how to use them).

Note

In the examples we are using the Word2007 writter (OOXML file) to increase the compatibility of our document. Besides, as a good practice in Symfony, you may want to keep your controller so thin as possible, so we recommend you to create services 

1. Streaming a dinamically generated file

Pitifully, the Phpword writter doesn't provide any method to stream a document, besides if it'd exist, it wouldn't be useful as we are working with Symfony. To return a generated file as a response, you will need to create it and then send it using the BinaryFileResponse class, however, we will not take care about the creation of a file somewhere in your server and then remove it. To make it easy, you can create a temporal file in the system using tempnam(sys_get_temp_dir(), "filename.extension").

The following controller create a word file and it is immediately returned as an attachment:

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

// Include the BinaryFileResponse and the ResponseHeaderBag
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

// Include the requires classes of Phpword
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\IOFactory;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction(Request $request)
    {
        // Create a new Word document
        $phpWord = new PhpWord();

        /* Note: any element you append to a document must reside inside of a Section. */

        // Adding an empty Section to the document...
        $section = $phpWord->addSection();
        // Adding Text element to the Section having font styled by default...
        $section->addText(
            '"Learn from yesterday, live for today, hope for tomorrow. '
            . 'The important thing is not to stop questioning." '
            . '(Albert Einstein)'
        );

        // Saving the document as OOXML file...
        $objWriter = IOFactory::createWriter($phpWord, 'Word2007');

        // Create a temporal file in the system
        $fileName = 'hello_world_download_file.docx';
        $temp_file = tempnam(sys_get_temp_dir(), $fileName);

        // Write in the temporal filepath
        $objWriter->save($temp_file);

        // Send the temporal file as response (as an attachment)
        $response = new BinaryFileResponse($temp_file);
        $response->setContentDisposition(
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
            $fileName
        );

        return $response;
    }
}

2. Save file in the server

By default you can write a generated word file in a local path using the save method of a writer and providing the destination path with filename as first argument:

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

// The response object
use Symfony\Component\HttpFoundation\Response;

// Include the requires classes of Phpword
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\IOFactory;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction(Request $request)
    {
        // Create a new Word document
        $phpWord = new PhpWord();

        /* Note: any element you append to a document must reside inside of a Section. */

        // Adding an empty Section to the document...
        $section = $phpWord->addSection();
        // Adding Text element to the Section having font styled by default...
        $section->addText(
            '"Learn from yesterday, live for today, hope for tomorrow. '
            . 'The important thing is not to stop questioning." '
            . '(Albert Einstein)'
        );

        // Saving the document as OOXML file...
        $objWriter = IOFactory::createWriter($phpWord, 'Word2007');

        $filePath = 'hello_world_serverfile.docx';
        // Write file into path
        $objWriter->save($filePath);

        return new Response("File succesfully written at $filePath");
    }
}

3. Inserting an image in a word document

To add an image you can use the addImage method available in the PhpWord class:

Note

In the example we are using the following demo image (earth.jpg) available in the official repository for test purposes.

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

// Return a text response
use Symfony\Component\HttpFoundation\Response;


// Include the requires classes of Phpword
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\IOFactory;
use PhpOffice\PhpWord\Style\Image;
use PhpOffice\PhpWord\Shared\Converter;
use PhpOffice\PhpWord\SimpleType\Jc;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction(Request $request)
    {

        $phpWord = new PhpWord();

        // Get the path the the images folder in Symfony
        $imagesFolder = $this->get('kernel')->getRootDir() . '/../web/images/';
        $_local_image_path = $imagesFolder.'earth.jpg';

        // Begin code
        $section = $phpWord->addSection();
        $section->addText('Local image without any styles:');
        $section->addImage($_local_image_path);
        $section->addTextBreak(2);
        $section->addText('Local image with styles:');
        $section->addImage($_local_image_path, array('width' => 210, 'height' => 210, 'alignment' => Jc::CENTER));
        $section->addTextBreak(2);

        // 1) Add a remote image
        $source = 'http://php.net/images/logos/php-med-trans-light.gif';
        $section->addText("Remote image from: {$source}");
        $section->addImage($source);

        // Wrapping style
        $text = str_repeat('Hello World! ', 15);
        $wrappingStyles = array('inline', 'behind', 'infront', 'square', 'tight');
        foreach ($wrappingStyles as $wrappingStyle) {
            $section->addTextBreak(5);
            $section->addText("Wrapping style {$wrappingStyle}");
            $section->addImage(
                $_local_image_path,
                array(
                    'positioning'   => 'relative',
                    'marginTop'     => -1,
                    'marginLeft'    => 1,
                    'width'         => 80,
                    'height'        => 80,
                    'wrappingStyle' => $wrappingStyle,
                )
            );
            $section->addText($text);
        }

        //Absolute positioning
        $section->addTextBreak(3);
        $section->addText('Absolute positioning: see top right corner of page');
        $section->addImage(
            $_local_image_path,
            array(
                'width'            => Converter::cmToPixel(3),
                'height'           => Converter::cmToPixel(3),
                'positioning'      => Image::POSITION_ABSOLUTE,
                'posHorizontal'    => Image::POSITION_HORIZONTAL_RIGHT,
                'posHorizontalRel' => Image::POSITION_RELATIVE_TO_PAGE,
                'posVerticalRel'   => Image::POSITION_RELATIVE_TO_PAGE,
                'marginLeft'       => Converter::cmToPixel(15.5),
                'marginTop'        => Converter::cmToPixel(1.55),
            )
        );

        //Relative positioning
        $section->addTextBreak(3);
        $section->addText('Relative positioning: Horizontal position center relative to column,');
        $section->addText('Vertical position top relative to line');
        $section->addImage(
            $_local_image_path,
            array(
                'width'            => Converter::cmToPixel(3),
                'height'           => Converter::cmToPixel(3),
                'positioning'      => Image::POSITION_RELATIVE,
                'posHorizontal'    => Image::POSITION_HORIZONTAL_CENTER,
                'posHorizontalRel' => Image::POSITION_RELATIVE_TO_COLUMN,
                'posVertical'      => Image::POSITION_VERTICAL_TOP,
                'posVerticalRel'   => Image::POSITION_RELATIVE_TO_LINE,
            )
        );

        // Saving the document as OOXML file...
        $objWriter = IOFactory::createWriter($phpWord, 'Word2007');

        $filePath = 'hello_world_images.docx';
        // Write file into path
        $objWriter->save($filePath);

        return new Response("File succesfully written at $filePath");
    }
}

4. Inserting charts in a word file

In Word charts are ideal for presenting information graphically and it's pretty easy to add them with PHPWord too (EMUs are used for coordinates in vector-based drawings and embedded pictures):

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

// Return a text response
use Symfony\Component\HttpFoundation\Response;


// Include the requires classes of Phpword
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\IOFactory;
use PhpOffice\PhpWord\Shared\Converter;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction(Request $request)
    {

        $phpWord = new PhpWord();

        // Define styles
        $phpWord->addTitleStyle(1, array('size' => 14, 'bold' => true), array('keepNext' => true, 'spaceBefore' => 240));
        $phpWord->addTitleStyle(2, array('size' => 14, 'bold' => true), array('keepNext' => true, 'spaceBefore' => 240));


        // 2D charts
        $section = $phpWord->addSection();
        $section->addTitle('2D charts', 1);
        $section = $phpWord->addSection(array('colsNum' => 2, 'breakType' => 'continuous'));
        $chartTypes = array('pie', 'doughnut', 'bar', 'column', 'line', 'area', 'scatter', 'radar');
        $twoSeries = array('bar', 'column', 'line', 'area', 'scatter', 'radar');
        $threeSeries = array('bar', 'line');
        $categories = array('A', 'B', 'C', 'D', 'E');
        $series1 = array(1, 3, 2, 5, 4);
        $series2 = array(3, 1, 7, 2, 6);
        $series3 = array(8, 3, 2, 5, 4);

        foreach ($chartTypes as $chartType) {
            $section->addTitle(ucfirst($chartType), 2);
            $chart = $section->addChart($chartType, $categories, $series1);
            $chart->getStyle()->setWidth(Converter::inchToEmu(2.5))->setHeight(Converter::inchToEmu(2));
            if (in_array($chartType, $twoSeries)) {
                $chart->addSeries($categories, $series2);
            }
            if (in_array($chartType, $threeSeries)) {
                $chart->addSeries($categories, $series3);
            }
            $section->addTextBreak();
        }

        // 3D charts
        $section = $phpWord->addSection(array('breakType' => 'continuous'));
        $section->addTitle('3D charts', 1);
        $section = $phpWord->addSection(array('colsNum' => 2, 'breakType' => 'continuous'));
        $chartTypes = array('pie', 'bar', 'column', 'line', 'area');
        $multiSeries = array('bar', 'column', 'line', 'area');
        $style = array('width' => Converter::cmToEmu(5), 'height' => Converter::cmToEmu(4), '3d' => true);

        foreach ($chartTypes as $chartType) {

            $section->addTitle(ucfirst($chartType), 2);

            $chart = $section->addChart($chartType, $categories, $series1, $style);

            if (in_array($chartType, $multiSeries)) {
                $chart->addSeries($categories, $series2);
                $chart->addSeries($categories, $series3);
            }

            $section->addTextBreak();
        }


        // Saving the document as OOXML file...
        $objWriter = IOFactory::createWriter($phpWord, 'Word2007');

        $filePath = 'hello_world_charts.docx';
        // Write file into path
        $objWriter->save($filePath);

        return new Response("File succesfully written at $filePath");
    }
}

We recommend you to read the documentation to see all the awesome things that you can do with PHPWord.

Happy coding !

Become a more social person