PHPOffice / PHPWord

A pure PHP library for reading and writing word processing documents
https://phpoffice.github.io/PHPWord/
Other
7.23k stars 2.69k forks source link

Cannot save a word document as PDF #2426

Open RosiersRobin opened 1 year ago

RosiersRobin commented 1 year ago

Describe the Bug

When trying to modify a word document (has a header and footer text in it) then the modification works perfectly. However, the word document needs to be a PDF. When I try to save it as PDF, I get Cannot add Title in Header.

Steps to Reproduce

Please provide a code sample that reproduces the issue.

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

$templateFile = storage_path('app/wordDocument.docx');
$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor($templateFile);

// set word values
$templateProcessor->setValue('{teamnr}', '123');

$outputFileDocx = storage_path('app/output_files/wordDocument.docx');
$templateProcessor->saveAs($outputFileDocx);

// load the word document again
$outputFilePdf = storage_path('app/output_files/pdf_file.pdf');
$phpWord = \PhpOffice\PhpWord\IOFactory::load($outputFileDocx);

//Save it into PDF
$PDFWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord,'PDF');
$PDFWriter->save($outputFilePdf);

Expected Behavior

I expect the word to be saved as a pdf file.

Current Behavior

Exception gets throwed:

 BadMethodCallException

  Cannot add Title in Header.

  at vendor\phpoffice\phpword\src\PhpWord\Element\AbstractContainer.php:270
    266▕
    267▕         // Check if a method is valid for current container
    268▕         if (isset($validContainers[$method])) {
    269▕             if (!in_array($this->container, $validContainers[$method])) {
  ➜ 270▕                 throw new BadMethodCallException("Cannot add {$method} in {$this->container}.");
    271▕             }
    272▕         }
    273▕
    274▕         // Check if a method is valid for current container, located in other container

  1   vendor\phpoffice\phpword\src\PhpWord\Element\AbstractContainer.php:135
      PhpOffice\PhpWord\Element\AbstractContainer::checkValidity("Title")

  2   vendor\phpoffice\phpword\src\PhpWord\Element\AbstractContainer.php:116
      PhpOffice\PhpWord\Element\AbstractContainer::addElement("Title", Object(PhpOffice\PhpWord\Element\TextRun), "1")

Context

Please fill in your environment information:

allestaire commented 11 months ago

You have to set the reader first, its either MPDF or DomPDF

\PhpOffice\PhpWord\Settings::setPdfRendererName(\PhpOffice\PhpWord\Settings::PDF_RENDERER_MPDF);
$phpWord = \PhpOffice\PhpWord\IOFactory::load(storage_path('generated.docx'),);
$xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'PDF');
$xmlWriter->save(storage_path('generated.pdf'));
RosiersRobin commented 11 months ago

@allestaire I've done as you suggested. When I don't replace anything, it works just fine. However, when I replace only 1 thing, it breaks.

to even better simulate, I have attached the word document it should change and save as pdf here: https://filetransfer.io/data-package/WNmNVR95#link

the full code I currently use:

<?php

use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\TemplateProcessor;

$domPdfPath = base_path('vendor/dompdf/dompdf');
    Settings::setPdfRendererPath($domPdfPath);
    Settings::setPdfRendererName('DomPDF');

    $templateFile = storage_path('app/documents/source_files/Acceptatiebrief.docx');
    $templateProcessor = new TemplateProcessor($templateFile);
    $templateProcessor->setValue('{teamnr}', '123');
    $templateProcessor->setValue('{strt_hr}', '18:31');
    $templateProcessor->setValue('{pilot_name}', 'DOE John');
    $templateProcessor->setValue('{pilot_license}', 'W123456R');
    $templateProcessor->setValue('{pilot_club}', 'Amazing club');
    $templateProcessor->setValue('{pilot_tel}', '+333333333');
    $templateProcessor->setValue('{copilot_name}', 'DOEDETTE Johanna');
    $templateProcessor->setValue('{copilot_license}', 'A65412D');
    $templateProcessor->setValue('{copilot_club}', 'Woops');
    $templateProcessor->setValue('{copilot_tel}', '');

    $outputFileDocx = storage_path('app/documents/output_files/Acceptatiebrief_test.docx');
    $templateProcessor->saveAs($outputFileDocx);

    \PhpOffice\PhpWord\Settings::setPdfRendererName(\PhpOffice\PhpWord\Settings::PDF_RENDERER_DOMPDF);
    $phpWord = \PhpOffice\PhpWord\IOFactory::load($outputFileDocx);
    $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'PDF');
    $xmlWriter->save(storage_path('app/documents/output_files/Acceptatiebrief_test.pdf'));

Any ideas on what I'm doing wrong?

allestaire commented 11 months ago

Seems like there is a problem with the docx format, Like when there are spaces or empty value, the other line will merge to top You should check first on how those elements being placed

Also can you share the original docx?

then converting docx to pdf is not totally reliable, as I tested it, it loses styles, and there are only I guess 80% of accuracy The problem might not be the template process but probabbly how the element is placed

RosiersRobin commented 11 months ago

@allestaire https://filetransfer.io/data-package/WNmNVR95#link this url contains the original docx file (with just the {replace} tags used.