Jeroen Moors

PHPWord is a library written in PHP that create word documents. No Windows operating system is needed for usage because the result are docx files (Office Open XML) that can be opened by all major office software.

While the library already contained a powerful templating system, it was not possible to add on the fly rows to a table.

As I required tables with a changing number of rows, I’ve added support for this. That was already quite some time ago, but still I get very often feedback on this feature. It seems to be that I wasn’t the only one who needed it.

PHPWord – Line breaks

Today I received an e-mail with a question, that I think, is a common issue with PHPWord: Adding line breaks in a text block.

Let’s assume you’ve got this text*:

Autumn end November: The night has fallen
The bare branches can be seen
Even more lonely

If you use a n (carriage return) or even a rn (line-feed and carriage return), your text will be shown in one single line. Don’t get misled by using LibreOffice to test your documents as it will interpret the new lines, while MS Word won’t!

A quick and easy fix to this is to replace the n by the OpenDocument tag <w:br/>:

$text = str_replace("n","n", $text);

*Credits for the haiku: Herman Van Rompuy, also known for being the President of the European Council.

PHPWord – Templates with repeating rows

PHPWord is a great library if you want to manipulate Word documents from PHP.

One of the easiest ways to create a document is to use a existing document as a template and the setValue() function to replace tags inside this document.

Unfortunately currently one very useful thing is missing: dynamically adding rows to a table. I’m not a rock-star programmer, I know some of them in person, but I’m not. Still I wrote a quick hack to overcome this issue.

This is how it works:

  1. Create a template Word document, containing the tables you would like to use.
    For each table, add one row to use as a template with the tags you would like to replace. Your template would be something like this: 

    First name Last name
    ${first_name} ${last_name}
  2. Call the cloneRow($tag, $numberOfRows) function before setValue() to duplicate your template row. Provide any tag on that row as $tag and the required number of rows as $number.
    Calling cloneRow('first_name', 3) would transform the working copy of your template into something like this: 

    First name Last name
    ${first_name#1} ${last_name#1}
    ${first_name#2} ${last_name#2}
    ${first_name#3} ${last_name#3}
  3. Now call setValue($tag, $value) adding #rowNumber to the original tags.
    To finish the example: 

    $doc->setValue('first_name#1', 'Jeroen');
    $doc->setValue('last_name#1', 'Moors');
    $doc->setValue('first_name#2', 'John');
    $doc->setValue('last_name#2', 'Doe');
    $doc->setValue('first_name#3', 'Jane');
    $doc->setValue('last_name#3', 'Roe');

    Would result in:

    First name Last name
    Jeroen Moors
    John Doe
    Jane Roe

To get this thing working you’ll have to replace the Template.php file found in side the PHPWord directory by this one Template.php.
Warning: don’t copy-paste it from the block below, as this might result in syntax errors.

     * Clone a table row
     * @param mixed $search
     * @param mixed $numberOfClones
	public function cloneRow($search, $numberOfClones) {
        if(substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
            $search = '${'.$search.'}';

		$tagPos 	 = strpos($this->_documentXML, $search);
		$rowStartPos = strrpos($this->_documentXML, "<w:tr", ((strlen($this-="">_documentXML) - $tagPos) * -1));
		$rowEndPos   = strpos($this->_documentXML, "", $tagPos) + 7;

		$result = substr($this->_documentXML, 0, $rowStartPos);
		$xmlRow = substr($this->_documentXML, $rowStartPos, ($rowEndPos - $rowStartPos));
		for ($i = 1; $i <= $numberOfClones; $i++) {
			$result .= preg_replace('/${(.*?)}/','${\1#'.$i.'}', $xmlRow);
		$result .= substr($this->_documentXML, $rowEndPos);

		$this->_documentXML = $result;

12.09.2013 – Thanks to Robert Pecha a bug has been fixed, please download the latest version of Template.php

Any feedback is welcome!