PHPWord icon indicating copy to clipboard operation
PHPWord copied to clipboard

If we load a docx using phpword then save it again, it should not be altered

Open rahal opened this issue 6 years ago • 10 comments

I set up a docx document with a header and a footer, and some basics stylings for paragraph and headings ( as well a numbering rules or headings ) , the main idea was to open the document, and add some simple html in the first section so I can get a clean docx file ( with header, footer, and pagination and the correct styles ) . Sadly, I lost the header, footer, font setup for default styles, obviously IOFactory::load doesn't load all those aspects and we won't be able to save it back correctly as expected ( and I don't know if it can save it if it was correctly loaded ) . I would have been very nice to be able to keep those aspects so we can be able to create easily ready to use docx documents.

Any idea how we could fix this? Thank you

Originally posted by @rahal in https://github.com/PHPOffice/PHPWord/issues/882#issuecomment-461780347

rahal avatar Feb 08 '19 12:02 rahal

Indeed, the IOFactory::load function unfortunately does not parse the whole docx specification. Your best option is to use the TemplateProcessor for this

troosan avatar Feb 11 '19 22:02 troosan

Yes, thanks, that's exactly what I did

rahal avatar Feb 22 '19 15:02 rahal

Hi @rahal,

How did you end up doing it with the template ? How do you make the html headings, etc use a set style in the docx ?

Thanks

chrisribe avatar Apr 24 '19 15:04 chrisribe

@rahal bump ;)

chrisribe avatar May 10 '19 22:05 chrisribe

Sorry.. I didn't saw the first notification. Well.. I prepare a docx template with all the styling, heading, footer, header, various setup as well as my "token" : content

``

use PhpOffice\PhpWord\IOFactory; use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\Settings as WordSettings; use PhpOffice\PhpWord\Shared\Html; use PhpOffice\PhpWord\TemplateProcessor; use PhpOffice\PhpWord\Writer\Word2007\Element\Container; use PhpOffice\Common\XMLWriter;

$templateProcessor = new TemplateProcessor($template); // let's get some xml content from our html $phpWord = new PhpWord('Word2007'); // Adding an empty Section to the document... $section = $phpWord->addSection(); Html::addHtml($section, $html, true, false); $writer = IOFactory::createWriter($phpWord, 'Word2007'); // convert the html to "word2017" xml $xmlWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY, './', WordSettings::hasCompatibility()); $containerWriter = new Container($xmlWriter, $section); $containerWriter->write(); $htmlAsXml = $xmlWriter->getData();

// remplace our templateContent WordSettings::setOutputEscapingEnabled(false); $templateProcessor->setValue('content', $htmlAsXml); WordSettings::setOutputEscapingEnabled(true);

// Save the file : $templateProcessor->saveAs($docxFilepath); ``

Something like that.. hope it helps By the way.. I works only with Microsoft Word ( the output didn't work correctly in libreoffice ) , if you have better luck, please let me know.

rahal avatar May 10 '19 23:05 rahal

@rahal thanks for the example will try it out and let you know.

chrisribe avatar May 23 '19 20:05 chrisribe

Hi @rahal ,

Your code works, I can finally add sections without breaking the templates !

Do you know if there is a way to apply custom styles that where set in the loaded template ex: MyStyle1. I see you can simply create H1, H2, H3 html tags for the different heading levels. I tried setting the class attribute to my html but that does not work. <p class='MyStyle1'>Hello</p>

What about tables ? Have you had any luck inserting those ? Or do you simply add <table><tr><td></td></tr></table> ?

Thanks a lot. Chris

chrisribe avatar Jun 14 '19 19:06 chrisribe

Glad my code could help! You're welcome. For the styles, you can simply setup the default styles in the template, that's what I did. As for the tables, I didn't need them, did you try using them? If it doesn't work, I think It can be possible to add support for table elements.

On Fri, Jun 14, 2019, 20:04 ChrisRibe [email protected] wrote:

Hi @rahal https://github.com/rahal ,

Your code works, I can finally add sections without breaking the templates !

Do you know if there is a way to apply custom styles that where set in the loaded template ex: MyStyle1. I see you can simply create H1, H2, H3 html tags for the different heading levels. I tried setting the class attribute to my html but that does not work.

Hello

What about tables ? Have you had any luck inserting those ? Or do you simply add ?

Thanks a lot. Chris

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/PHPOffice/PHPWord/issues/1571?email_source=notifications&email_token=AAAINYZXV5RZM3BRPK26GDTP2PTVFA5CNFSM4GV5CNM2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXXWF4A#issuecomment-502227696, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAINY7PFNQJJSFCNNBQHBDP2PTVFANCNFSM4GV5CNMQ .

rahal avatar Jun 14 '19 20:06 rahal

Well...

Seems you can use your trick to load just about anything into the template (tested title levels, tables, and text). Only thing for level styles you have to set them at least once in the template or else the styles do not stick.... odd. I also removed the Word2007 on the new PhpWord and it still works.

` use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\SimpleType\TblWidth; use PhpOffice\PhpWord\Style\Table; use PhpOffice\PhpWord\TemplateProcessor; use PhpOffice\PhpWord\Writer\Word2007\Element\Container; use PhpOffice\Common\XMLWriter;

//Solution to injet into templates without breaking things
function containerToXML($container){
	$xmlWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY, './', Settings::hasCompatibility());
	$containerWriter = new Container($xmlWriter, $container);
	$containerWriter->write();
	return($xmlWriter->getData());
}

//1st working template load and inject content via xml...
$templateProcessor = new TemplateProcessor("e:/the-template.docx");

//Try and build xml from objects and insert xml instead...
$phpWord = new PhpWord();

$fontStyle = array();//if no style given levels do not register (if empty array defaults to template)
$phpWord->addTitleStyle(1, $fontStyle);
$phpWord->addTitleStyle(2, $fontStyle);
$phpWord->addTitleStyle(3, $fontStyle);

$section = $phpWord->addSection();
for($i=0;$i < 25;$i++){
	$section->addText("Hello world $i");
}
//Table add test
$tbl = $section->addTable(array(
	"layout" => Table::LAYOUT_AUTO,
	"width"	 => 100 * 50, //in word 1% == 50 unit (with in 1/50)
	"unit"	 => TblWidth::PERCENT
));
$tbl->addRow(900, array('tblHeader' => true));
$tbl->addCell(150)->addText('Header1');
$tbl->addCell(150)->addText('Header2');
$tbl->addCell(150)->addText('Header3');
$tbl->addCell(150)->addText('Header4');
for($i=0;$i < 40;$i++){
	$tbl->addRow();
	$tbl->addCell(150)->addText("cell 1 row:$i");
	$tbl->addCell(150)->addText("cell 2 row:$i");
	$tbl->addCell(150)->addText("cell 3 row:$i");
	$tbl->addCell(150)->addText("cell 4 row:$i");
}

$section->addTitle('Level 1', 1);
$section->addTitle('Level 2', 2);
$section->addTitle('Level 3', 3);

$elXml = containerToXML($section);

// remplace our templateContent
Settings::setOutputEscapingEnabled(false);
$templateProcessor->setValue('content', $elXml);
Settings::setOutputEscapingEnabled(true);

// Save the file :
$templateProcessor->saveAs("e:/demo-xml-inject.docx");

`

@troosan could this be added to php word template processor so templates are more flexible ?

Thanks again @rahal

chrisribe avatar Jun 14 '19 21:06 chrisribe

You are welcome @chrisribe

rahal avatar Jun 17 '19 23:06 rahal