A pure PHP library for reading and writing word processing documents
Other
6393
316
2505

I'm trying to take an image from a folder with a function in Codeigniter 4 and want to export it to word using PHPWord assisted by a tutorial at #1895, but the results I get are Invalid Image when when I try to copy it to the url of the image it appears according to the directory.
please help, any kind of help would be very valuable.

I also embed the code as study material.
Thanks very much

`
public function exportBA()
{
$user_login = $this->AuthModel->getUserId();

    if (!isset($user_login['lp_sekretariat']) && !isset($user_login['user_lembaga_id'])) {
        $str = '  <script src="https://code.jquery.com/jquery-1.12.0.min.js"></script>
           <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
           <script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
           <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap3-dialog/1.35.1/js/bootstrap-dialog.min.js"></script>
           <script type="text/javascript">
               setTimeout(function() { 
                  BootstrapDialog.alert(\'Silahkan isi profil Anda dan Lembaga terlebih dahulu!!\') 
                  window.location.href = \'/profil_user\';
                },10000);
           </script>';
        echo $str;
    } else {

  
        $this->WilayahModel = new WilayahModel();
        $templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor(FCPATH . 'data/templates/ba_pdtt.docx');


        $filter1 = session()->get('kode_desa');
        $filter5 = '1';
   
        $jmlVerval = $this->VerivaliGeoModel->getJmlVerval($filter1);
        $jmlVerval = $jmlVerval['jml'];
    
        $jmlVervalFix = $this->VerivaliGeoModel->getJmlVervalFix($filter1, $filter5);
        $jmlVervalFix = $jmlVervalFix['jml'];

        $jmlNonVerval = $jmlVerval - $jmlVervalFix;
    
        $vervalPdtt = $this->VerivaliGeoModel->getVerivaliFix($filter1, $filter5);
    
        $coba = [];
        foreach ($vervalPdtt as $i => $value) {

            $coba[] = [
                'vg_no' => $i + 1,
                'vg_nik' => $value['vg_nik'],
                'vg_nama_lengkap' => $value['vg_nama_lengkap'],
                'vg_alamat' => $value['vg_alamat'],
                'vg_rt' => $value['vg_rt'],
                'vg_rw' => $value['vg_rw'],
                'vg_desa' => $value['namaDesa'],
                'dbj_nama_bansos' => $value['dbj_nama_bansos'],
                'vg_lat' => $value['vg_lat'],
                'vg_lang' => $value['vg_lang'],
                'vg_fp_name' => $value['vg_fp'],
                'vg_fp' => $value['vg_fp'],
                'vg_fr_name' => $value['vg_fr'],
                'vg_fr' => $value['vg_fr'],
            ];
            $i++;
        }
  

           $templateProcessor->cloneRowAndSetValues('vg_no', $coba);
        foreach ($coba as $i => $item) {
            // path php to folder
            $path = base_url('data/foto_pm/' . $item['vg_fp']);
            $templateProcessor->setImageValue(sprintf('gambar#%d', $i + 1), array('path' => $path, 'width' => 10, 'height' => 10, 'ratio' => false));

        }

       $filename = 'BA VERVAL PDTT.docx';

        header("Content-Description: File Transfer");
        header('Content-Disposition: attachment;filename="' . $filename . '"'); //tell browser what's the file name
        header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');
        header('Content-Transfer-Encoding: binary');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Expires: 0');

        // Saving the document as OOXML file...
        $templateProcessor->saveAs('php://output');

    }
}

2022-07-07_22h17_30
`

hi, I've been trying to host a project which includes PHPword. when trying to access the file that uses PHPword I'm having this error

Forbidden
You don't have permission to access this resource.

Additionally, a 403 Forbidden error was encountered while trying to use an ErrorDocument to handle the request.

When using a header or footer along with sections that span multiple pages, the header/footer is aligned on page 1, but then misaligns on subsequent pages because it appears to be inserted "inside" the section.

Is this a bug, or is there a way of forceably saying "Stick the header/footer on the base of the page ignoring any sections"

Page 1 - Aligned footer
TestFile - Screenshot Page 1

Page 2 - Footer missaligned and is inside the "section"
TestFile - Screenshot Page 2

Example Document
TestFile.docx

Minimal code to replicate

<?php

namespace App\Http\Controllers;

class PlaylistExportController extends Controller
{
    public function generateRandomString($length = 10) {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $charactersLength = strlen($characters);
        $randomString = '';
        for ($i = 0; $i < $length; $i++) {
            $randomString .= $characters[rand(0, $charactersLength - 1)];
        }
        return $randomString;
    }

    public function index($type)
    {
        $phpWord = new \PhpOffice\PhpWord\PhpWord();
        $phpWord->getCompatibility()->setOoxmlVersion(15);

        $tableStyle = new \PhpOffice\PhpWord\Style\Table;
        $tableStyle->setUnit(\PhpOffice\PhpWord\Style\Table::WIDTH_PERCENT);
        $tableStyle->setWidth(100 * 50);

        $headerFooterTableStyle = new \PhpOffice\PhpWord\Style\Table;
        $headerFooterTableStyle->setUnit(\PhpOffice\PhpWord\Style\Table::WIDTH_PERCENT);
        $headerFooterTableStyle->setWidth(100 * 50);

        for ($y = 0; $y <= 3; $y++) {
            $section = $phpWord->addSection(['marginLeft' => 300, 'marginRight' => 300, 'marginTop' => 600, 'breakType' => 'continuous']);

            $footer = $section->addFooter();
            $footertable = $footer->addTable($headerFooterTableStyle);
            $footertable->addRow();
            $footertable->addCell(2000)->addPreserveText('{Page {PAGE} of {NUMPAGES}');
            $footertable->addCell(2000)->addText('Generated '.date('d/m/y h:i:s A'));

            $section->addText('Section  - ' . $this->generateRandomString() . '<w:br/>');

            $tablesection = $phpWord->addSection([
                'colsNum' => 2,
                'colsSpace' => 100,
                'breakType' => 'continuous',
            ]);
            $table = $tablesection->addTable();

            for ($x = 0; $x <= 200; $x++) {
                $table->addRow();
                $table->addCell()->addText($this->generateRandomString());
            }
            $tablesection->addPageBreak();
        }

        $phpWord->save('TestFile.docx', 'Word2007', true);
    }
}

Describe the Bug

A clear and concise description of what the bug is.

Steps to Reproduce

Please provide a code sample that reproduces the issue.

addSection(); $section->addText( $article_disclosure ); // XML Writer compatibility \PhpOffice\PhpWord\Settings::setCompatibility(false); \PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(true); $obj_writer = \PhpOffice\PhpWord\IOFactory::createWriter( $phpWord ); $upload_dir = wp_upload_dir(); $file_url = $upload_dir['path'] . '/'. 'file-saved' . '.docx'; $file_path = $upload_dir['url'] . '/'. 'file-saved' . '.docx'; ob_clean(); $obj_writer->save( $file_url ); ?>

Expected Behavior

In my logic i need to read file, after that i need to add new paragraph in it and save new file with added format. When i do it then images styles are broken.

Current Behavior

What is the current behavior?

Context

Please fill in your environment information:

  • php version 8

if (strpos($target, '#') === 0) {

This should be prefaced with (string) or else a warning can be produced.

I am using draftjs and phpword. Fmom draftjs had a json objects using that i am constructing a word file. After that i am trying to merge the target file and template file. After merge the template converting both source and target file into XML format. Using this writing the target file filled with the contents stored in source document.

<?php
require_once(CONFIG);
$listFormatNumOL = 1;
$listFormatNumUL = 1;

function convertKeysToValues($str, $keytoval){
	$toreplace = array();
	$replaceby = array();
	$lineHasSomeEmptyKey = 0;
	
	$newstr = $str;
	foreach($keytoval AS $k=>$v){		
		if(trim($v['value']) != ''){
			array_push($toreplace, $k);
			if($v['type'] == 2)
			{
				if(!checkIfOnlyUnderscore(trim($v['value'])))
				{
					$dayval = explode('/', trim($v['value']));
					
					if(substr($k, 2, 1) == '$'){
						$df = date_format(date_create($dayval[2].'-'.$dayval[1].'-'.$dayval[0]), substr($k, 2, -2));
						$df = explode('$', $df);
						$dtnum = substr($df[1], 0, -2);
						$dtext = substr($df[1], -2, 2);
						unset($df[0]);
						unset($df[1]);
						$df = implode('$', $df);
						array_push($replaceby, $dtnum.'<sup>'.$dtext.'<sup>'.$df);
					}
					else
						array_push($replaceby, date_format(date_create($dayval[2].'-'.$dayval[1].'-'.$dayval[0]), substr($k, 2, -2)));
				}
				else
					array_push($replaceby, trim($v['value']));
			}
			else{
				array_push($replaceby, trim($v['value']));
			}
		}
		else{
			
			array_push($toreplace, $k);
			array_push($replaceby, trim($v['value']));
			if(stripos($str, $k) > -1)
				$lineHasSomeEmptyKey = 1;
		}
	}
}
function RemoveSpecialChar($value){
	$result  = preg_replace('/[^a-zA-Z0-9 -]/s','',$value);

	return $result;
}
function afterClearingExtraSpaces($arr, $demographics){
	foreach($arr AS $l=>$m){
		$ert = convertKeysToValues(str_replace(array("([[","]])","[[","]]"),"",$m), $demographics);
		$arr[$l] = $ert[0];
	}
	
	$fullstr = implode('', $arr);
	$allkeys = explode('((', $fullstr);
	$puncValues = array();
	$spaceAtLeft = 0;
	foreach($allkeys AS $k=>$v){
		$reqval = explode('))', $v);
		if(sizeof($reqval) > 1){
			array_push($puncValues, $spaceAtLeft == 1?'':$reqval[0]);
			if(mb_substr($reqval[1], 0, 1) == " " || mb_substr($reqval[1], 0, 1) == "	"){
				if(sizeof($puncValues) > 0){
					for($tr = sizeof($puncValues)-1; $tr >= 0; $tr--){
						if($puncValues[$tr] != ""){
							$puncValues[$tr] = "";
							break;
						}
					}
				}
			}
		}
		
		
		if($k == 0 && ($v == '' || $v == '	'))
			$spaceAtLeft = 1;
		else if(sizeof($reqval)==2 && $reqval[1]=='')
			$spaceAtLeft = 1;
		else if(mb_substr($v, -1, 1) == ' ')
			$spaceAtLeft = 1;
		else
			$spaceAtLeft = 0;
	}
	
	if($spaceAtLeft == 1){
		if(sizeof($puncValues) > 0){
			for($tr = sizeof($puncValues)-1; $tr >= 0; $tr--){
				//echo $tr.':'.$puncValues[$tr].'|';
				if($puncValues[$tr] != ""){
					$puncValues[$tr] = "";
					break;
				}
			}
		}
	}
	
	foreach($arr AS $l=>$m){
		$finalstring = '';
		$newReqVal = explode('((', $m);
		if(sizeof($newReqVal) > 1)
		{
			$finalstring = $newReqVal[0];
			foreach($newReqVal AS $k=>$v){
				if($k > 0){
					$morebreak = explode('))', $v);					
					if(sizeof($morebreak) > 1)
						$finalstring .= array_shift($puncValues).$morebreak[1];					
				}
			}
			$arr[$l] = $finalstring;
			
			
		}
	}
	
	return $arr;
}


function convertToProperCase($str, $arr){
	if(isset($arr["capitalize"]))
		return ucwords($str);
	return $str;
}

function checkIfOnlyUnderscore($str){
	// print_r("hi");
	$newstr = str_split_unicode(trim($str), 1);
	
	foreach($newstr AS $k=>$v){
		if($v != '_')
			return false;
	}

	return true;
}


function xmlEntities($str)
{
    return $str;
}

function makeWordStrings($section, $decoded, $demographics, $calledSnapshot, $tabSize, $defaultIndent){
	$removeBlanks = 0;
	$lineStays = 0;
	$allBlanks = array();
	$blanksCondition = array();
		
	foreach($decoded['blocks'] AS $k=>$v){
		$styles = array();
		$offset = array();
		
		$breakOffset = array();
		$breakOffset[0] = 1;
		$text = $v['text'];
		
		$breakOffset[mb_strlen($text)] = 1;
		foreach($v['inlineStyleRanges'] AS $l=>$m){
			$breakOffset[$m['offset']] = 1;
			$breakOffset[$m['offset'] + $m['length']] = 1;
		}
		
		$breakOffset = array_keys($breakOffset);
		
		sort($breakOffset); 
		
		
		for($i=1; $i<sizeof($breakOffset); $i++){
			foreach($v['inlineStyleRanges'] AS $l=>$m){
				if($m['offset'] <= $breakOffset[$i-1] && ($m['offset']+$m['length']) >= $breakOffset[$i]){
					if(!isset($styles[$breakOffset[$i-1]]))
						$styles[$breakOffset[$i-1]] = array();
					$wer = getStyle($m['style']);
					$styles[$breakOffset[$i-1]][$wer[0]] = $wer[1];
					$offset[$breakOffset[$i-1]] = $breakOffset[$i] - $breakOffset[$i-1];
				}
			}
		}
		
		
		ksort($offset);
		
		
		$strarr = array();
		
		if($text == "*****PAGE BREAK - TYPE FROM NEXT LINE*****"){
			$section -> addPageBreak();
			continue;
		}
		if($text == '{{content}}'){
			$newindent = $defaultIndent;
			foreach($v['data'] AS $l=>$m){
				foreach($m AS $x=>$y){
					if($x == 'indentation')
						$newindent += intval($y>0?$y:0);
				}
			}
			makeWordStrings($section, $calledSnapshot, $demographics, '', $tabSize, $newindent);
			continue;
		}
		
		
		if(mb_stripos($text, '([[') === 0){
			$removeBlanks = 1;
			$lineStays = 1;
		}
		else if(mb_stripos($text, '[[') === 0){
			$removeBlanks = 1;
		}
		
		
		if(!empty($offset))
		{
			$it = 0;
			
			foreach($offset AS $l=>$m){
				$strarr[$it] = mb_substr($text, $it, $l-$it);
				$strarr[$l] = mb_substr($text, $l, $m);
				$it = $l + $m;
				
			}
			$strarr[$it] = mb_substr($text, $it);
		}
		else{
			$strarr[0] = $text;
		}		
		
		$paragraphStyles = array('lineHeight'=>1, 'indentation'=>array('left'=>intval(ceil($tabSize * $defaultIndent * 15 * 48 / 1.27))));
		foreach($v['data'] AS $l=>$m){
			foreach($m AS $x=>$y){
				if($x == 'lineHeight')
					$paragraphStyles['lineHeight'] = $y;
				else if($x == 'indentation'){
					$paragraphStyles['indentation']['left'] = intval(ceil($tabSize * ($y + $defaultIndent) * 15 * 48 / 1.27));
				}
				else if($x == 'hanging'){
					$paragraphStyles['indentation']['hanging'] = intval(ceil($tabSize * $y * 15 * 48 / 1.27));
					$paragraphStyles['indentation']['left'] = intval(ceil($tabSize * ($y + $defaultIndent) * 15 * 48 / 1.27));
				}
				else if($x == 'textAlign')
				{
					switch($y){
						case 'left':
							$paragraphStyles['alignment'] = 'left';
							break;
						case 'right':
							$paragraphStyles['alignment'] = 'right';
							break;
						case 'center':
							$paragraphStyles['alignment'] = 'center';
							break;
						case 'justify':
							$paragraphStyles['alignment'] = 'both';
							break;
					}
					
				}
			}
		}
		
		if($lineStays == 1){
			array_push($blanksCondition, convertKeysToValues($text, $demographics)[1]);
			array_push($allBlanks, array($v, $strarr, $paragraphStyles, $offset, $styles));
		}
		else if($removeBlanks == 1){
			$bg = convertKeysToValues($text, $demographics);
			if($bg[1] == 0){
				$section = addToDoc($section, $v, $strarr, $paragraphStyles, $demographics, $offset, $styles, $decoded, $tabSize, $defaultIndent);
			}
		}
		else
			$section = addToDoc($section, $v, $strarr, $paragraphStyles, $demographics, $offset, $styles, $decoded, $tabSize, $defaultIndent);
		
		$removeBlanks = 0;
		
		if(mb_stripos($text, ']])') + 3 == mb_strlen($text)){
			if(!in_array(0, $blanksCondition))
			{
				foreach($allBlanks AS $n=>$m){
					$section = addToDoc($section, $m[0], array(''), $m[2], $demographics, array(), $m[4], $decoded, $tabSize, $defaultIndent);
				}
			}
			else{
				foreach($allBlanks AS $n=>$m){
					if($blanksCondition[$n] == 0){
						$section = addToDoc($section, $m[0], $m[1], $m[2], $demographics, $m[3], $m[4], $decoded, $tabSize, $defaultIndent);
					}
				}
			}
			
			$lineStays = 0;
			$allBlanks = array();
			$blanksCondition = array();
		}
		
	}
}

function str_split_unicode($str, $l = 0) {
    if ($l > 0) {
        $ret = array();
        $len = mb_strlen($str, "UTF-8");
        for ($i = 0; $i < $len; $i += $l) {
            $ret[] = mb_substr($str, $i, $l, "UTF-8");
        }
        return $ret;
    }
    return preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
}

function addToDoc($section, $v, $strarr, $paragraphStyles, $demographics, $offset, $styles, $decoded, $tabSize, $defaultIndent){
	global $phpWord;
	global $listFormatNumOL, $listFormatNumUL;
	$moveahead = 1;
	
	$newindent = $defaultIndent;
	foreach($v['data'] AS $l=>$m){
		foreach($m AS $x=>$y){
			if($x == 'indentation')
				$newindent += intval($y>0?$y:0);
		}
	}
	$phpWord->addNumberingStyle(
		'ordered_list-'.$listFormatNumOL,
		array(
			'type'   => 'singleLevel',
			'levels' => array(
				array('format' => 'decimal', 'text' => '%1.', 'left' => 360, 'hanging' => 360, 'tabPos' => 360),
                array('format' => 'upperLetter', 'text' => '%2.', 'left' => 720, 'hanging' => 360, 'tabPos' => 720),
			),
		)
	);

	$predefinedMultilevel = array('listType' => \PhpOffice\PhpWord\Style\ListItem::TYPE_BULLET_FILLED);
	
	
	
	foreach($v['entityRanges'] AS $t=>$h){
		
		if($decoded['entityMap'][$h['key']]['type'] == 'TABLE')
		{
			$moveahead = 0;
			$tabledata = $decoded['entityMap'][$h['key']]['data'];
			$table = $section->addTable(array('borderSize' => 6, 'borderColor' => '999999', 'indent'=> new PhpOffice\PhpWord\ComplexType\TblWidth(ceil($tabSize * $newindent * 15 * 48 / 1.27)), 'width'=>100*50 - ceil($tabSize * $newindent * 15 * 48 * 20 / (50 * 1.27)), 'unit'=>\PhpOffice\PhpWord\Style\Table::WIDTH_PERCENT, 'layout'=>PhpOffice\PhpWord\Style\Table::LAYOUT_FIXED));
			foreach($tabledata['table'] AS $z=>$p){
				$table->addRow();
				foreach($p AS $t=>$f){
					$celltr = $table->addCell();
					$celltr = makeWordStrings($celltr, json_decode($f, true), $demographics, '', $tabSize, 0);
				}
			} 
			
		}
	}
	
	if($moveahead){
		if($v['type'] == 'unstyled'){
			$listFormatNumUL++;
			$listFormatNumOL++;
					
			if(sizeof($strarr)==1 && $strarr[0]==''){
				$erstyle = array();
				foreach($v['inlineStyleRanges'] AS $l=>$m){
					$wer = getStyle($m['style']);
					$erstyle[$wer[0]] = $wer[1];
				}
				if(!empty($erstyle))
				{
					$section->addTextBreak(1, $erstyle, $paragraphStyles);
				}
				else
					$section->addTextBreak(1, null, $paragraphStyles);
			}
			else{
				$startOffset = 0;
				
				$comments = array();
				
				foreach($v['entityRanges'] AS $t=>$h){
					if($decoded['entityMap'][$h['key']]['type'] == 'COMMENT')
					{
						$commentdata = $decoded['entityMap'][$h['key']]['data'];
						foreach($commentdata['comment'] AS $z=>$p){
							$comm = new \PhpOffice\PhpWord\Element\Comment($p['user'], new \DateTime(), 'my_initials');
							$comm->addText($p['comment']);
							array_push($comments, array('start'=>$h['offset'], 'end'=>$h['offset']+$h['length'] - 1, 'comm'=>$comm));
						}
						
					}
				}
				
				$textrun = $section->addTextRun($paragraphStyles);
				
				
				
				$strarr = afterClearingExtraSpaces($strarr, $demographics);
				
				foreach($strarr AS $l=>$m){
					$democonv = $m;
					if(mb_strlen($democonv) > 0){						
						$supbreak = explode('<sup>', $democonv);
						if(sizeof($supbreak) > 1){
							$newst = $styles[$l];
							$newst['superScript'] = true;
							$textrun->addText($supbreak[0], $styles[$l], array('tabs' => array(new \PhpOffice\PhpWord\Style\Tab('left', 1550))));
							$textrun->addText($supbreak[1], $newst, array('tabs' => array(new \PhpOffice\PhpWord\Style\Tab('left', 1550))));
							$textrun->addText($supbreak[2], $styles[$l], array('tabs' => array(new \PhpOffice\PhpWord\Style\Tab('left', 1550))));
						}
						else{
							$breakstring = str_split_unicode($democonv, 1);
							
							$nextcapital = 1;
							
							foreach($breakstring AS $z=>$p){
								if(!isset($offset[$l])){
									$retext = xmlEntities($p);
									
									if($retext == "\n")
										$textToComment = $textrun->addTextBreak();
									else
										$textToComment = $textrun->addText($retext, null, array('tabs' => array(new \PhpOffice\PhpWord\Style\Tab('left', 1550))));
								}
								else{									
									$retext = xmlEntities($nextcapital?convertToProperCase($p, $styles[$l]):$p);
									
									if($retext == "\n")
										$textToComment = $textrun->addTextBreak();
									else
										$textToComment = $textrun->addText($retext, $styles[$l], array('tabs' => array(new \PhpOffice\PhpWord\Style\Tab('left', 1550))));
								}
								
								foreach($comments AS $q=>$w){
									if($w['start'] == $z+$startOffset+$q)
										$w['comm']->setStartElement($textToComment);
									if($w['end'] == $z+$startOffset-$q)
										$w['comm']->setEndElement($textToComment);
								}
								
								if($p == ' ')
									$nextcapital = 1;
								else
									$nextcapital = 0;
							}
							
							$startOffset += sizeof($breakstring);
						}
						
						
					}
				}
				
				foreach($comments AS $q=>$w){
					$phpWord->addComment($w['comm']);
				}
			}
		}

		if($v['type'] == 'unordered-list-item'){
			$listFormatNumUL++;
			
			$tempPS = $paragraphStyles;
			unset($tempPS['indentation']);
			
			$listItemRun = $section->addListItemRun($v['depth'], $predefinedMultilevel, $tempPS);
			
			
			if(sizeof($strarr)==1 && $strarr[0]==''){
				$erstyle = array();
				foreach($v['inlineStyleRanges'] AS $l=>$m){
					$wer = getStyle($m['style']);
					$erstyle[$wer[0]] = $wer[1];
				}
				if(!empty($erstyle))
					$listItemRun->addText("", $erstyle);
				else
					$listItemRun->addText("");
			}
			else{
				$startOffset = 0;
				
				$comments = array();
				
				foreach($v['entityRanges'] AS $t=>$h){
					if($decoded['entityMap'][$h['key']]['type'] == 'COMMENT')
					{
						$commentdata = $decoded['entityMap'][$h['key']]['data'];
						foreach($commentdata['comment'] AS $z=>$p){
							$comm = new \PhpOffice\PhpWord\Element\Comment($p['user'], new \DateTime(), 'my_initials');
							$comm->addText($p['comment']);
							array_push($comments, array('start'=>$h['offset'], 'end'=>$h['offset']+$h['length'] - 1, 'comm'=>$comm));
						}
						
					}
				}
				$strarr = afterClearingExtraSpaces($strarr, $demographics);
				foreach($strarr AS $l=>$m){
					$democonv = $m;
					if(mb_strlen($democonv) > 0){
					
						$breakstring = str_split_unicode($democonv, 1);
						
						$nextcapital = 1;
						
						foreach($breakstring AS $z=>$p){
						
							if(!isset($offset[$l])){
								$retext = xmlEntities($p);
								
								if($retext == "\n")
									$textToComment = $listItemRun->addTextBreak();
								else
									$textToComment = $listItemRun->addText($retext, null, array('tabs' => array(new \PhpOffice\PhpWord\Style\Tab('left', 1550))));
							}
							else{
								$retext = xmlEntities($nextcapital?convertToProperCase($p, $styles[$l]):$p);
								
								if($retext == "\n")
									$textToComment = $listItemRun->addTextBreak();
								else
									$textToComment = $listItemRun->addText($retext, $styles[$l], array('tabs' => array(new \PhpOffice\PhpWord\Style\Tab('left', 1550))));
							}
							foreach($comments AS $q=>$w){
								if($w['start'] == $z+$startOffset+$q)
									$w['comm']->setStartElement($textToComment);
								if($w['end'] == $z+$startOffset-$q)
									$w['comm']->setEndElement($textToComment);
							}
							
							if($p == ' ')
								$nextcapital = 1;
							else
								$nextcapital = 0;
						}
						
						$startOffset += sizeof($breakstring);
					}
				}
				foreach($comments AS $q=>$w){
					$phpWord->addComment($w['comm']);
				}
			}
			
		}

		if($v['type'] == 'ordered-list-item'){
			
			$listFormatNumUL++;
			$tempPS = $paragraphStyles;
			unset($tempPS['indentation']);
			
			$listItemRun = $section->addListItemRun($v['depth'], 'ordered_list-'.$listFormatNumOL, $tempPS);
			if(sizeof($strarr)==1 && $strarr[0]==''){
				$erstyle = array();
				foreach($v['inlineStyleRanges'] AS $l=>$m){
					$wer = getStyle($m['style']);
					$erstyle[$wer[0]] = $wer[1];
				}
				if(!empty($erstyle))
					$listItemRun->addText("", $erstyle);
				else
					$listItemRun->addText("");
			}
			else{
				$startOffset = 0;
				
				$comments = array();
				foreach($v['entityRanges'] AS $t=>$h){
					if($decoded['entityMap'][$h['key']]['type'] == 'COMMENT')
					{
						$commentdata = $decoded['entityMap'][$h['key']]['data'];
						foreach($commentdata['comment'] AS $z=>$p){
							$comm = new \PhpOffice\PhpWord\Element\Comment($p['user'], new \DateTime(), 'my_initials');
							$comm->addText($p['comment']);
							array_push($comments, array('start'=>$h['offset'], 'end'=>$h['offset']+$h['length'] - 1, 'comm'=>$comm));
						}
						
					}
				}
				$strarr = afterClearingExtraSpaces($strarr, $demographics);
				foreach($strarr AS $l=>$m){
					$democonv = $m;
					if(mb_strlen($democonv) > 0){
						$breakstring = str_split_unicode($democonv, 1);
						
						$nextcapital = 1;
						
						foreach($breakstring AS $z=>$p){
							if(!isset($offset[$l])){
								$retext = xmlEntities($p);
								
								if($retext == "\n"){
									$textToComment = $listItemRun->addTextBreak();
								
								}
								else
									$textToComment = $listItemRun->addText($retext, null, array('tabs' => array(new \PhpOffice\PhpWord\Style\Tab('left', 1550))));
								
							}
							else{
								$retext = xmlEntities($nextcapital?convertToProperCase($p, $styles[$l]):$p);
								
								if($retext == "\n"){
									$textToComment = $listItemRun->addTextBreak();
								}
								else
									$textToComment = $listItemRun->addText($retext, $styles[$l], array('tabs' => array(new \PhpOffice\PhpWord\Style\Tab('left', 1550))));
							}
							foreach($comments AS $q=>$w){
								if($w['start'] == $z+$startOffset+$q)
									$w['comm']->setStartElement($textToComment);
								if($w['end'] == $z+$startOffset-$q)
									$w['comm']->setEndElement($textToComment);
							}
							
							if($p == ' ')
								$nextcapital = 1;
							else
								$nextcapital = 0;
						}
						
						$startOffset += sizeof($breakstring);
					}
				}
				foreach($comments AS $q=>$w){
					$phpWord->addComment($w['comm']);
				}
			}
		}

		if($v['type'] == 'atomic'){
			
			$listFormatNumUL++;
			$listFormatNumOL++;
			
			foreach($v['entityRanges'] AS $l=>$m){
				$imagedata = $decoded['entityMap'][$m['key']]['data'];
				
				$section->addImage(
					$imagedata['src'],
					array("width"=>floor(getimagesize($imagedata['src'])[0] / 1.33))
				);
			}
			
		}
		
		
	}
	return $section;
}

function getStyle($style){ 
	if(stripos($style, "FONTSIZE-") === 0){
		$val = substr($style, 9);
		if($val == 'DEFAULT')
			return array('size', null);
		return array('size', intval($val));
	}
	else if(stripos($style, "FONTCOLOR-") === 0){
		$val = explode('#', $style);
		return array('color', $val[1]);
	}
	else{
		switch($style){
			case 'UPPERCASE':
				return array('allCaps', true);
			case 'BOLD':
				return array('bold', true);
			case 'SUPERSCRIPT':
				return array('superScript', true);
			case 'SUBSCRIPT':
				return array('subScript', true);
			case 'UNDERLINE':
				return array('underline', 'single');
			case 'CAPITALIZE':
				return array('capitalize', true);
			case 'ITALIC':
				return array('italic', true);
			case 'LOWERCASE':
				return array('lowercase', true);
			case 'STRIKETHROUGH':
				return array('strikethrough', true);
			case 'HIGHLIGHT':
				return array('fgColor', 'yellow');
			case 'FONTFAMILY-TIMES NEW ROMAN':
				return array('name', 'Times New Roman');
			case 'FONTFAMILY-ARIAL':
				return array('name', 'Arial');
			case 'FONTFAMILY-ARIAL ROUNDED MT BOLD':
				return array('name', 'Arial Rounded MT Bold');
			case 'FONTFAMILY-ARIAL UNICODE MS':
				return array('name', 'Arial Unicode MS');
			case 'FONTFAMILY-BAHNSCHRIFT':
				return array('name', 'Bahnschrift');
			case 'FONTFAMILY-BAHNSCHRIFT SEMIBOLD':
				return array('name', 'Bahnschrift Semibold');
			case 'FONTFAMILY-BOOK ANTIQUA':
				return array('name', 'Book Antiqua');
			case 'FONTFAMILY-BOOKMAN OLD STYLE':
				return array('name', 'Bookman Old Style');
			case 'FONTFAMILY-CALIBRI':
				return array('name', 'Calibri');
			case 'FONTFAMILY-CALIBRI LIGHT':
				return array('name', 'Calibri Light');
			case 'FONTFAMILY-CAMBRIA':
				return array('name', 'Cambria');
			case 'FONTFAMILY-CENTURY GOTHIC':
				return array('name', 'Century Gothic');
			case 'FONTFAMILY-COURIER NEW':
				return array('name', 'Courier New');
			case 'FONTFAMILY-DEJAVU SERIF':
				return array('name', 'DejaVu Serif');
			case 'FONTFAMILY-FRANKLIN GOTHIC MEDIUM':
				return array('name', 'Franklin Gothic Medium');
			case 'FONTFAMILY-FREESTYLE SCRIPT':
				return array('name', 'Freestyle Script');
			case 'FONTFAMILY-GARAMOND':
				return array('name', 'Garamond');
			case 'FONTFAMILY-IMPACT':
				return array('name', 'Impact');
			case 'FONTFAMILY-HELVETICA':
				return array('name', 'Helvetica');
			case 'FONTFAMILY-MICROSOFT SANS SERIF':
				return array('name', 'Microsoft Sans Serif');
			case 'FONTFAMILY-PALATINO LINOTYPE':
				return array('name', 'Palatino Linotype');
			case 'FONTFAMILY-SANS-SERIF':
				return array('name', 'Sans-Serif');
			case 'FONTFAMILY-TAHOMA':
				return array('name', 'Tahoma');
			case 'FONTFAMILY-TREBUCHET MS':
				return array('name', 'Trebuchet MS');
			case 'FONTFAMILY-VERDANA':
				return array('name', 'Verdana');
			case 'FONTFAMILY-ARIALMT':
				return array('name', 'ArialMT');
			case 'FONTFAMILY-GENEVA':
				return array('name', 'Geneva');
		}
	}
}




$phpWord = null;
function generateTranscriptionTaskWordFile($tid, $storageDirectory, $whichPart, $includeNoName=0){
	global $dbc;
	global $phpWord;
	
	$query = mysqli_query($dbc, "SELECT name, dictator_id FROM task WHERE id=$tid LIMIT 1");
	$filename = null;
	$dict_id = 0;
	while($query && $result = mysqli_fetch_array($query, MYSQLI_ASSOC)){
		$filename = stripcslashes($result['name']);
		$dict_id = mysqli_fetch_array(mysqli_query($dbc, "SELECT name FROM users WHERE id=".$result['dictator_id']." AND users.access IN (2,12) LIMIT 1"), MYSQLI_ASSOC);
		$dict_id = $dict_id['name'];
	}
	
	if($dict_id && mb_strlen($dict_id) > 0){
		if($whichPart == 'ALL')
			$query = mysqli_query($dbc, "SELECT id, file_part_number, snapshot, template_id FROM task_history WHERE id IN (SELECT MAX(id) AS id FROM task_history WHERE task_id=$tid GROUP BY file_part_number)");
		else
			$query = mysqli_query($dbc, "SELECT id, file_part_number, snapshot, template_id FROM task_history WHERE id IN (SELECT MAX(id) AS id FROM task_history WHERE task_id=$tid GROUP BY file_part_number) AND file_part_number IN (".implode(',', $whichPart).")");
		
		$rcnt = 0;
		
		$wordFilesArray = array();
		
		while($query && $result = mysqli_fetch_array($query, MYSQLI_ASSOC))
		{
			$rcnt++;
			if($result['template_id']){
				
				$newquery = mysqli_query($dbc, "SELECT snapshot, header from template_history WHERE template_id=".$result['template_id']." ORDER BY id DESC LIMIT 1");
				$newresult = mysqli_fetch_array($newquery, MYSQLI_ASSOC);
				
				$tabSize = mysqli_fetch_array(mysqli_query($dbc, "SELECT tabsize FROM template WHERE id=".$result['template_id']), MYSQLI_ASSOC)['tabsize'];
				
				$demographics_query = mysqli_query($dbc, "SELECT template_demo_val.value AS value, template_demo.demo_key AS keyu, template_demo.demo_name AS keyn, template_demo.demo_type AS type FROM template_demo_val INNER JOIN template_demo ON template_demo_val.tempdemo_id=template_demo.id WHERE template_demo_val.task_id=$tid AND template_demo_val.part_num=".$result['file_part_number']."");
				
				$demographics = array();
				
				while($demographics_query && $newres = mysqli_fetch_array($demographics_query, MYSQLI_ASSOC)){
					$demographics[$newres['keyu']] = array('value'=>$newres['value'], 'type'=>$newres['type'], 'name'=>$newres['keyn']);
				}
				
				if(!empty($newresult['header']) && !empty($result['snapshot']))
				{
					\PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(true);
					$phpWord = new \PhpOffice\PhpWord\PhpWord();
					$section = $phpWord->addSection();
					
					if(!empty($result['snapshot']))
					{
						$decoded = json_decode($result['snapshot'], true);
						for($i = sizeof($decoded['blocks']) - 1; $i>= 0; $i--){
							if($decoded['blocks'][$i]['text'] != '')
								break;
							else{
								array_pop($decoded['blocks']);
							}
							
						}
						makeWordStrings($section, json_decode($result['snapshot'], true), $demographics, $decoded, $tabSize, 0);
					}
					
					$newfilename = $filename;
					if(mysqli_num_rows($query) > 1){
						if($result['file_part_number'] > 1)
							$newfilename .= '_'.(strlen($result['file_part_number']-1)==1?'0'.($result['file_part_number']-1):($result['file_part_number']-1));
						
					}
					$phpWord->save(WITHOUTTEMPLATE."$newfilename.docx", 'Word2007');
					
					$templateFile  = TEMPLATES.$newresult['header'];
					$generatedFile = WITHOUTTEMPLATE."$newfilename.docx";
					
					$targetFile    = $storageDirectory.($includeNoName==1?"":"$newfilename.docx");
					copy($templateFile, $targetFile);
					$targetZip = new ZipArchive();
					if ($targetZip->open($targetFile, ZipArchive::CREATE) !== true) {
						return false;
					}
					$targetDocument = $targetZip->getFromName('word/document.xml');
					$targetRelationsDocument = $targetZip->getFromName('word/_rels/document.xml.rels');
					$targetCorePropsDocument = $targetZip->getFromName('docProps/core.xml');
					$targetCustomPropsDocument = $targetZip->getFromName('docProps/custom.xml');
					$targetContentTypesDocument = $targetZip->getFromName('[Content_Types].xml');
					$targetContentTypesDocuments = $targetZip->getFromName('word/numbering.xml');
					
					$targetDom      = new DOMDocument();
					$targetDom->loadXML($targetDocument);
					$targetCorePropsDom = new DOMDocument();
					$targetCorePropsDom->loadXML($targetCorePropsDocument);					
					
					$targetContentTypesDom = new DOMDocument();
					$targetContentTypesDom->loadXML($targetContentTypesDocument);
					
					$targetXPath = new \DOMXPath($targetDom);
					$targetXPath->registerNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
					
					 
					// open source
					$sourceZip = new \ZipArchive();
					$sourceZip->open($generatedFile, ZipArchive::CREATE);
					$sourceDocument = $sourceZip->getFromName('word/document.xml');
					$sourceRelationsDocument = $sourceZip->getFromName('word/_rels/document.xml.rels');
					$sourceContentTypesDocument = $sourceZip->getFromName('[Content_Types].xml');
					$sourceContentTypesDocuments = $sourceZip->getFromName('word/numbering.xml');
					
					$sourceDom      = new DOMDocument();
					$sourceDom->loadXML($sourceDocument);
					
					$sourceRelationsDom = new DOMDocument();
					$sourceRelationsDom->loadXML($sourceRelationsDocument);

					$sourceContentTypes = new DOMDocument();
					$sourceContentTypes->loadXML($sourceContentTypesDocument);
					
					$sourceXPath = new \DOMXPath($sourceDom);
					$sourceXPath->registerNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
					
					$sourceRelationsXPath = new \DOMXPath($sourceRelationsDom);
					 
					$replacementMarkerNode = $targetXPath->query('//w:p[contains(translate(normalize-space(), " ", ""),"$CONTENT$")]')[0];
					$sourceNodes = $sourceXPath->query('//w:document//w:body/*[not(self::w:sectPr)]');					
					$relationsArr = array();
					$startId = 700;
					
					$ifThereAreComments = 0;
					$ifThereAreNumber = 0;
					
					for ($i = 0; $i < $sourceZip->numFiles; $i++) {
						if(strpos($sourceZip->getNameIndex($i), 'media') > 0 || strpos($sourceZip->getNameIndex($i), 'comments.xml') > 0){
							if(strpos($sourceZip->getNameIndex($i), 'comments.xml') > 0)
								$ifThereAreComments = 1;
							
							$imagefile = $sourceZip->getFromName($sourceZip->getNameIndex($i));
							$temp = fopen(TEMPORARYFILES.substr(bin2hex(random_bytes(5)),0, 5), "w");
							fwrite($temp, $imagefile);  
							fseek($temp, 0);
							
							$targetZip -> addFile(stream_get_meta_data($temp)['uri'], $sourceZip->getNameIndex($i));
						}
						if(strpos($sourceZip->getNameIndex($i), 'numbering.xml') > 0){
							$ifThereAreNumber = 1;
						}
							
					}		
					
					
					foreach ($sourceNodes as $sourceNode) {
						$checkifImage = $sourceNode -> getElementsByTagNameNS ("urn:schemas-microsoft-com:vml", 'imagedata');
						foreach($checkifImage AS $t)
						{
							$rid = $t->getAttribute('r:id');
							$relationsArr[$rid] = array('id'=>'rId'.$startId, 'media'=>'');
							$t->setAttribute('r:id', $relationsArr[$rid]['id']);
							$startId++;
						}
						$imported = $replacementMarkerNode->ownerDocument->importNode($sourceNode, true);
						$inserted = $replacementMarkerNode->parentNode->insertBefore($imported, $replacementMarkerNode);
					}
					$replacementMarkerNode->parentNode->removeChild($replacementMarkerNode); 
					$targetZip->addFromString('word/document.xml', $targetDom->saveXML());
					
					$getSourceRelationsTree = $sourceRelationsDom -> getElementsByTagName('Relationship');
					foreach($getSourceRelationsTree AS $t){
						$rid = $t->getAttribute('Id');
						if(isset($relationsArr[$rid])){
							$relationsArr[$rid]['media'] = $t->getAttribute('Target');
						}
					}
					
					if($ifThereAreComments){
						$targetRelationsDom = new DOMDocument();
						$targetRelationsDom->loadXML($targetRelationsDocument);
					
						$parentDom = $targetRelationsDom->getElementsByTagName('Relationships');
						$relationsElement = $targetRelationsDom->createElement("Relationship");
						
						$domAttribute = $targetRelationsDom->createAttribute('Id');
						$domAttribute->value = 'rId1000';
						$relationsElement->appendChild($domAttribute);
						
						$domAttribute = $targetRelationsDom->createAttribute('Target');
						$domAttribute->value = 'comments.xml';
						$relationsElement->appendChild($domAttribute);
						$domAttribute = $targetRelationsDom->createAttribute('Type');
						$domAttribute->value = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments";
						$relationsElement->appendChild($domAttribute);
						
						$parentDom[0]->appendChild($relationsElement);
						
						$targetZip->addFromString('word/_rels/document.xml.rels', $targetRelationsDom->saveXML());
						
						
						$parentDom = $targetContentTypesDom->getElementsByTagName('Types');
						$relationsElement = $targetContentTypesDom->createElement("Override");
						
						$domAttribute = $targetContentTypesDom->createAttribute('PartName');
						$domAttribute->value = '/word/comments.xml';
						$relationsElement->appendChild($domAttribute);
						
						$domAttribute = $targetContentTypesDom->createAttribute('ContentType');
						$domAttribute->value = 'application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml';
						$relationsElement->appendChild($domAttribute);
						
						$parentDom[0]->appendChild($relationsElement);
						
						$targetZip->addFromString('[Content_Types].xml', $targetContentTypesDom->saveXML());
						
						
					}
					if(!empty($relationsArr)){
						$targetRelationsDom = new DOMDocument();
						$targetRelationsDom->loadXML($targetRelationsDocument);
					
						$parentDom = $targetRelationsDom -> getElementsByTagName('Relationships');
						foreach($relationsArr AS $t){
							$relationsElement = $targetRelationsDom->createElement("Relationship");
							
							$domAttribute = $targetRelationsDom->createAttribute('Id');
							$domAttribute->value = $t['id'];
							$relationsElement->appendChild($domAttribute);
							
							$domAttribute = $targetRelationsDom->createAttribute('Target');
							$domAttribute->value = $t['media'];
							$relationsElement->appendChild($domAttribute);
							
							$domAttribute = $targetRelationsDom->createAttribute('Type');
							$domAttribute->value = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
							$relationsElement->appendChild($domAttribute);
							
							$parentDom[0]->appendChild($relationsElement);
						}
						
						$targetZip->addFromString('word/_rels/document.xml.rels', $targetRelationsDom->saveXML());
					}
					
					$parentDom = $targetCorePropsDom -> getElementsByTagNameNS("http://schemas.openxmlformats.org/package/2006/metadata/core-properties", 'coreProperties');
					$toRem = $parentDom[0]->getElementsByTagNameNS("http://purl.org/dc/elements/1.1/", 'title')->Item(0);
					$relationsElement = $targetCorePropsDom->createElement("dc:title", isset($demographics['{{title}}'])?$demographics['{{title}}']['value']:null);
						
					$parentDom[0]->appendChild($relationsElement);
					$toRem->parentNode->removeChild($toRem);
					
					$toRem = $parentDom[0]->getElementsByTagNameNS("http://purl.org/dc/elements/1.1/", 'subject')->Item(0);
					$relationsElement = $targetCorePropsDom->createElement("dc:subject", $dict_id);
						
					$parentDom[0]->appendChild($relationsElement);
					$toRem->parentNode->removeChild($toRem);
					
					
					$targetZip->addFromString('docProps/core.xml', $targetCorePropsDom->saveXML());
					
					
					
					
					if($targetCustomPropsDocument)
					{
						$targetCustomPropsDom = new DOMDocument();
						$targetCustomPropsDom->loadXML($targetCustomPropsDocument);
						
						$parentDom = $targetCustomPropsDom -> getElementsByTagName('Properties');
						$allProper = $parentDom[0]->getElementsByTagName('property');
						foreach($allProper AS $t){
							switch($t -> getAttribute('name')){
								case 'TEST_1':
									if(isset($demographics['{{mrn}}']) || isset($demographics['{{intg-code}}'])){
										if(isset($demographics['{{mrn}}'])){
											if(isset($demographics['{{intg-code}}'])){
												if(!checkIfOnlyUnderscore($demographics['{{intg-code}}']['value'])){
													$toRem = $t -> getElementsByTagNameNS("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes", 'lpwstr')->Item(0);
													$relationsElement = $targetCustomPropsDom->createElement("vt:lpwstr", $demographics['{{intg-code}}']['value']);
													$t->appendChild($relationsElement);
													$toRem->parentNode->removeChild($toRem);
												}
											}
											else{
												if(!checkIfOnlyUnderscore($demographics['{{mrn}}']['value'])){
													$toRem = $t -> getElementsByTagNameNS("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes", 'lpwstr')->Item(0);
													$relationsElement = $targetCustomPropsDom->createElement("vt:lpwstr", $demographics['{{mrn}}']['value']);
													$t->appendChild($relationsElement);
													$toRem->parentNode->removeChild($toRem);
												}
											}
										}
									}
									break;
								case 'TEST_2':
										$toRem = $t -> getElementsByTagNameNS("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes", 'bool')->Item(0);
										$relationsElement = $targetCustomPropsDom->createElement("vt:bool", $result['file_part_number']==1?1:0);
										$t->appendChild($relationsElement);
										$toRem->parentNode->removeChild($toRem);
									break;
								case 'TEST_3':
									if(isset($demographics['{{report-type}}'])){
										if(!checkIfOnlyUnderscore($demographics['{{report-type}}']['value'])){
											$toRem = $t -> getElementsByTagNameNS("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes", 'lpwstr')->Item(0);
											$relationsElement = $targetCustomPropsDom->createElement("vt:lpwstr", RemoveSpecialChar($demographics['{{report-type}}']['value']));
											$t->appendChild($relationsElement);
											$toRem->parentNode->removeChild($toRem);
										}
									}
									break;
								case 'TEST_4':
									if(isset($demographics['{{student-f-n}}'])){
										if(!checkIfOnlyUnderscore($demographics['{{student-f-n}}']['value'])){
											$toRem = $t -> getElementsByTagNameNS("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes", 'lpwstr')->Item(0);
											$relationsElement = $targetCustomPropsDom->createElement("vt:lpwstr", RemoveSpecialChar($demographics['{{student-f-n}}']['value']));
											$t->appendChild($relationsElement);
											$toRem->parentNode->removeChild($toRem);
										}
									}
									break;
								case 'TEST_5':
									if(isset($demographics['{{student-l-n}}'])){
										if(!checkIfOnlyUnderscore($demographics['{{student-l-n}}']['value'])){
											$toRem = $t -> getElementsByTagNameNS("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes", 'lpwstr')->Item(0);
											$relationsElement = $targetCustomPropsDom->createElement("vt:lpwstr", RemoveSpecialChar($demographics['{{student-l-n}}']['value']));
											$t->appendChild($relationsElement);
											$toRem->parentNode->removeChild($toRem);
										}
									}
									break;
								case 'TEST_6':
									if(isset($demographics['{{student-m-n}}'])){
										if(!checkIfOnlyUnderscore($demographics['{{student-m-n}}']['value'])){
											$toRem = $t -> getElementsByTagNameNS("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes", 'lpwstr')->Item(0);
											$relationsElement = $targetCustomPropsDom->createElement("vt:lpwstr", RemoveSpecialChar($demographics['{{student-m-n}}']['value']));
											$t->appendChild($relationsElement);
											$toRem->parentNode->removeChild($toRem);
										}
									}
									break;
								case 'TEST_7':
									foreach($demographics AS $k=>$v){
										if(stripos($v['name'], 'birth') > -1 && $v['type']==2)
										{
											if(!checkIfOnlyUnderscore($v['value'])){
												$toRem = $t -> getElementsByTagNameNS("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes", 'lpwstr')->Item(0);
												$relationsElement = $targetCustomPropsDom->createElement("vt:lpwstr", $v['value']);
												$t->appendChild($relationsElement);
												$toRem->parentNode->removeChild($toRem);
												break;
											}
										}
									}
									break;
								case 'TEST_8':
									foreach($demographics AS $k=>$v){
										if(stripos($v['name'], 'visit') > -1 && $v['type']==2)
										{
											if(!checkIfOnlyUnderscore($v['value'])){
												$toRem = $t -> getElementsByTagNameNS("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes", 'lpwstr')->Item(0);
												$relationsElement = $targetCustomPropsDom->createElement("vt:lpwstr", $v['value']);
												$t->appendChild($relationsElement);
												$toRem->parentNode->removeChild($toRem);
												break;
											}
										}
									}
									break;
							}
						}
						
						
						$targetZip->addFromString('docProps/custom.xml', $targetCustomPropsDom->saveXML());
					}

					
					$targetZip->close();
					
					$wordFilesArray[$result['file_part_number']] = $targetFile;
				}
				
			}
		} 
		
		return array('r' => 1, 'd'=>array('totFileParts'=>$rcnt, 'wordFiles'=>$wordFilesArray));
	}
	
	
	return array('r'=>0, 'e'=>'INVALID_ACTIVITY');
}

This is:

  • [√] a bug report
  • [√] a feature request

Expected Behavior

Support EMF image.

Failure Information

Throws PhpOffice\PhpWord\Exception\InvalidImageException exception.
Exception message :
Invalid image: zip:///Users/xxx/Downloads/xxxx.docx#word/media/image.emf
#0 /works/shared/laravel/vendor/phpoffice/phpword/src/PhpWord/Element/Image.php(149): PhpOffice\PhpWord\Element\Image->checkImage()
#1 [internal function]: PhpOffice\PhpWord\Element\Image->__construct('zip:///Users/hu...', NULL, false, 'Picture 18')

How to Reproduce

Document file contains emf format images.
Google emf I got this page: https://fileinfo.com/extension/emf

<?php
use PhpOffice\PhpWord\IOFactory;
$file = '/path/to/file.docx';
$phpWord = IOFactory::load($file);
$sections = $phpWord->getSections();
foreach ($sections as $section) {
      $elements = $section->getElements();
      foreach ($elements as $element) {
            // do something else...
      }
}

Context

  • PHP version: PHP 7.1.16
  • PHPWord version: 0.15.0

Hi,
I need to create a word file from PHP. I create a HTML string into PHP and use PhpWord to create a file

`$phpWord = new \PhpOffice\PhpWord\PhpWord();

\PhpOffice\PhpWord\Shared\Html::addHtml($section, $html);
`

This is my simple table:

unfortunately I can't share the table code in this post

`

`

Unfortunatly the column width is not recognized. the column width is adapted to the content.

How can I solve the problem? Could you please help me?

Thanks
L

Nome e Cognome
Luogo e data di nascita

word to html using writer lose style data and image size or borders and microsoft office header and footer text.
as a result, this conversion is incomplete.

Describe the Bug

LI tag with inline style ""text-align:justify" does not justify text inside LI element.

Steps to Reproduce

<ol>
	<li style="text-align:justify;">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam..</li>
</ol>

Possible solution

/PhpWord/Shared/Html.php

    protected static function parseListItem($node, $element, &$styles, $data)
    {
        $cNodes = $node->childNodes;
        if (!empty($cNodes)) {
            
             /*FIX issue with incorrect style parsing  of LI element*/
             $styles['paragraph'] = self::recursiveParseStylesInHierarchy($node, $styles['paragraph']);
            /**/

            $listRun = $element->addListItemRun($data['listdepth'], $styles['list'], $styles['paragraph']);
            foreach ($cNodes as $cNode) {
                self::parseNode($cNode, $listRun, $styles, $data);
            }
        }
    }

Context

  • PHP Version: 7.3.1
  • PHPWord Version: 0.18.3

Hello, what? I would like to know if it is possible with PhpOffice Word, to build a document with "addShape", rect, what happens is that I need to create a series of 4 x 8 squares, with one divided with spaces. As if it were a format for square labels.

Yes, please, could you advise me or guide me? I would appreciate it

Regards!

Hi there,

I am trying to replace a text with a bullet numbered text but have not found a way yet. I tried placing a bullet using the PHPWord's variable and line break of but it did not insert a bullet point on a new line and only replaces the text.

I will be thankful if someone can help me out.


Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

Describe the Bug

In word 2007 and below, Left-to-right columns are displayed right-to-left (not text).

In word 2007 and below:
image

In word 2019:
image

Steps to Reproduce

<?php
require __DIR__ . '/vendor/autoload.php';

$phpWord = new \PhpOffice\PhpWord\PhpWord();
$section = $phpWord->addSection();
$section->addTextBreak();
$section->addText("表2 A2组测试项目", $fTableCaptionStyle, $pTableCaptionStyle);
$tableStyle = [
	'borderSize'      => 6,
	'cellMarginLeft'  => 567 * 0.19,
	'cellMarginRight' => 567 * 0.19,
	'layout'          => 'fixed',
];
$table = $section->addTable($tableStyle);
$table->addRow(30 * 11);
$table->addCell(60 * 11, $baseCellStyle)->addText('序号', $fCellStyle, $pCellStyle);
$table->addCell(60 * 11, $baseCellStyle)->addText('选项', $fCellStyle, $pCellStyle);
$table->addCell(220 * 11, $baseCellStyle)->addText('测试项目', $fCellStyle, $pCellStyle);
$table->addCell(222 * 11, $baseCellStyle)->addText('测试条件', $fCellStyle, $pCellStyle);
$table->addCell(100 * 11, $baseCellStyle)->addText('引用条款', $fCellStyle, $pCellStyle);
$table->addCell(255 * 11, $baseCellStyle)->addText('备注', $fCellStyle, $pCellStyle);

Context

Please fill in your environment information:

  • PHP Version: PHP7.3
  • PHPWord Version: 0.18.1

I have been trying to use phpword to edit a docx in laravel and it seems impossible. Is there a solution to it?

Hi
ist it possible to insert a pagebreak inside a processed document?

thanks!
Alex

Describe the Bug

The method setChartValue() is undefined

Steps to Reproduce

Using this from documentation :
source : [https://phpword.readthedocs.io/en/latest/templates-processing.html]

$templateProcessor- = new \PhpOffice\PhpWord\TemplateProcessor("mytemplate.docx');

$categories = array('A', 'B', 'C', 'D', 'E');
$series1 = array(1, 3, 2, 5, 4);
$chart = new Chart('doughnut', $categories, $series1);
$templateProcessor->setChartValue('myChart', $chart);
$templateProcessor->save();

Expected Behavior

I expect a graph to appear in replacement of ${myChart} into the template

Current Behavior

Fatal error: Uncaught Error: Call to undefined method PhpOffice\PhpWord\TemplateProcessor::setChartValue()

Context

Please fill in your environment information:

  • PHP Version:7.2.7
  • PHPWord Version:0.18.3

作者你好,我是在无互联网的环境下开发部署PHPword的,请问怎样才能离线安装PHPword呢?有没有便捷的安装包?

Have a loop which inserts html from a database. When a second ordered list is added (in a new section), the numbering does not restart. For example:

foreach ($rows as $row) {
    $section->addTitle(htmlspecialchars($row->title), 2);
    \PhpOffice\PhpWord\Shared\Html::addHtml($section, $row->somehtml);
}

Might result in something like this:

Section Title
bla bla

  1. list item
  2. list itme

New section title
text bla bla
3. list item
4. list item

I have to go into Word to manually set the starting number to 1.

I will see if I can dig into the code and find where this is set.


Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

Describe the Bug

Using setComplexBlock or setComplexValue remove all other placeholder than the one being searched

Steps to Reproduce

This code is used to add html string from WYSIWYG editor to template. First html is added to section and then making placeholder with the same number as elements in the section and saving it to a file. Afterwards, the file is loaded using second template processor and setComplexBlock is used to add the elements.

$phpWord = new PhpWord();
$section = $phpWord->addSection();
    
//$data is array of data fetched from database
Html::addHtml($section, $data['provision_current']);
$elements = $section->getElements();

$placeArray = [];
for($i = 0; $i<count($elements); $i++)
{
    $placeArray[$i] = '${place'.$i.'}';
}
$placeholder = implode("<w:br/>", $placeArray);

$templateProc = new TemplateProcessor(storage_path('Template.docx'));

$templateProc->setValue('here', $placeholder);

$templateProc->saveAs($file);
$templateSecond = new TemplateProcessor($file);
for($j = 0; $j<count($elements); $j++)
{
    $templateSecond->setComplexBlock($placeArray[$j], $elements[$j]);
}

$templateSecond->saveAs($file);

Expected Behavior

All elements of section replace all placeholder

Current Behavior

Only first element is replacing the placeholder. All other placeholders are somehow removed afterwards.

Context

Please fill in your environment information:

  • PHP Version: 7.4.13
  • PHPWord Version: 0.18.3
  • Implemented on Lumen Version 8.3.1

PHP version: 8.1
PHPWord version: 0.18.3
DomPDF versions: 1.1.0 - 1.2.2

PHPWord since its first versions generates page breaks with non-standard <pagebreak> tag. It worked in pair with DomPDF perfectly till version 1.1.0. Since then DomPDF started checking "blockyness" of the elements for some of its internal logic (I've checked diff 1.0.2...1.1.0). Since <pagebreak> tag is unknown to DomPDF, it treats it as inline and don't add page break in resulting PDF document.

This commit added following line:
https://github.com/dompdf/dompdf/blob/aa594c1cdbcdab04977fdd0fff669a017fb50ef4/src/FrameDecorator/Page.php#L182

Sample HTML for testing (simplified version of PHPWord-generated content):

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<p>so close...</p>
<pagebreak style="page-break-before: always;" pagebreak="true"></pagebreak>
<!--<pagebreak style="display: block; page-break-before: always;" pagebreak="true"></pagebreak>-->
<!--<div style="page-break-before: always;" pagebreak="true"></div>-->
<p>...yet so far (or?)</p>
</body>
</html>

You can test it in DomPDF debugger. Output between versions 1.0.2 and 1.1.1 differs.
Uncomment to make sure that blocky elements works fine.
Adding pagebreak { display: block } tag to html.css helps too.

This bug report is mirrored in DomPDF repository.