PhpWord 输出在 LibreOffice 中不起作用,但在 MS Word 中工作正常

PhpWord output doesn't work in LibreOffice but works fine in MS Word

这与 GitHub 页面 [link] 上列出的一个半已知问题有关,其中 PhpWord 生成表格的方式导致它将单元格宽度设置为“”,这不是在 LibreOffice 中有效,但在 Word 中没问题。

GitHub 上的问题具体列出了 html 到单词的转换,但我在使用常规的面向对象接口时也遇到了这个问题。

我试过手动设置单元格宽度,但似乎没有任何效果。

我想问问是否有人对此问题有任何解决方法,因为它几乎不可能在无法访问 MS Word 的情况下在 Linux 框中调试我的输出。我需要为客户导出 .docx。

为什么不直接导出到 .odt 来测试呢?如果我这样做,它也不会呈现我在页面上的列表.....

这是我正在使用的文档的粗略大纲(由于保密协议,我不能随意分享确切的代码):

$document = new PhpWord\PhpWord();
$section = $document->addSection();

// This list doesn't get rendered in the ODT file
$section->addListItem('Employee ID: 98765');
$section->addListItem('First Name: John');
$section->addListItem('Last Name: Doe');
$section->addListItem('SSN: 12345');

// This table has errors in LibreOffice when exported to .docx
if ($show_adv_details) {
    $table = $section->addTable();
    $table->addRow();
    $table->addCell()->addText('SSN');
    $table->addCell()->addText('Email');
    $table->addCell()->addText('Phone No');
    
    $table->addRow();
    $table->addCell()->addText('12345');
    $table->addCell()->addText('jd@example.com');
    $table->addCell()->addText('999 1234');

    // repeat ad nauseam
}

$section->addTitle('Comment');
$section->addTitle('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.');

我在 Ubuntu.

上使用 PHP 7.0(古老的遗留系统,对此无能为力)

最后在这里重申一下这个问题:

我可以做些什么来使该文档在所有不同的输出上正确呈现吗?该文档似乎没有太大帮助。

好吧,我不是 PHPOffice 包的维护者,但这是我在包代码中挖掘并解决你的两个问题的解决方法:

  1. ODT 列表不起作用;
  2. 使用 LibreOffice 打开时 Word2007 Writer 上的表格不起作用。

我意识到 ListItem.php class 在 ODText writer 上不存在,我想这就是为什么你不能将列表添加到 . odt 文件。

您需要手动将宽度添加到列中以使其在 LibreOffice 上工作(如 Nigel 所说,您可以通过每个 addCell() 调用并添加一些数字,即使是“1”也可以).

如何解决这两个问题

我们将使用一些修复问题的更新“覆盖”原始文件。 为了让它工作,你应该使用 Composer 安装 PHPWord(这是安装指南中唯一可用的选项,但还有其他方法)。

没有 Composer,在 PHPWord require 之后 require 自定义文件(我认为它有效)。或者直接更改文件所在位置(一个坏主意)。

文件

创建这样的结构:

Custom
├── Element
│   └── Table.php
└── Writer
    └── ODText
        └── Element
            └── ListItem.php

当然,你可以使用任何其他的,但我们要“覆盖”原始包文件,所以我保留了它的结构。

我们将使用 Autoload Files 获取这些文件:

composer.json:

...
"autoload": {
    "files": [
        "Custom/Element/Table.php",
        "Custom/Writer/ODText/Element/ListItem.php"
    ]
},
...

如果您的 composer.json 上没有“自动加载”键,您必须添加它,“文件”键也是如此。

运行 composer dump-autoload 更新更改。

自定义 classes

现在,我们必须将代码添加到自定义文件中。

Custom/Writer/ODText/Element/ListItem.php:

<?php
namespace PhpOffice\PhpWord\Writer\ODText\Element;

/**
 * ListItem element writer
 */
class ListItem extends AbstractElement
{
    /**
     * Write list item element.
     */
    public function write()
    {
        $xmlWriter = $this->getXmlWriter();
        $element = $this->getElement();
        if (!$element instanceof \PhpOffice\PhpWord\Element\ListItem) {
            return;
        }
        
        $textObject = $element->getTextObject();

        $xmlWriter->startElement('text:list');
        $xmlWriter->writeAttribute('text:style-name', 'L1');

        $xmlWriter->startElement('text:list-item');

        $xmlWriter->startElement('text:p');
        $xmlWriter->writeAttribute('text:style-name', 'P1');

        $elementWriter = new Text($xmlWriter, $textObject, true);
        $elementWriter->write();

        $xmlWriter->endElement(); // text:list
        $xmlWriter->endElement(); // text:p
        $xmlWriter->endElement(); // text:list-item
    }
}

文件由Word2007版本改编而来,解决了您的第一个问题。现在列表将适用于 ODText。

Custom/Element/Table.php:

<?php
namespace PhpOffice\PhpWord\Element;

use PhpOffice\PhpWord\Style\Table as TableStyle;

/**
 * Table element writer
 */
class Table extends AbstractElement
{
    /**
     * Table style
     *
     * @var \PhpOffice\PhpWord\Style\Table
     */
    private $style;

    /**
     * Table rows
     *
     * @var \PhpOffice\PhpWord\Element\Row[]
     */
    private $rows = array();

    /**
     * Table width
     *
     * @var int
     */
    private $width = null;

    /**
     * Create a new table
     *
     * @param mixed $style
     */
    public function __construct($style = null)
    {
        $this->style = $this->setNewStyle(new TableStyle(), $style);
    }

    /**
     * Add a row
     *
     * @param int $height
     * @param mixed $style
     * @return \PhpOffice\PhpWord\Element\Row
     */
    public function addRow($height = null, $style = null)
    {
        $row = new Row($height, $style);
        $row->setParentContainer($this);
        $this->rows[] = $row;

        return $row;
    }

    /**
     * Add a cell
     *
     * @param int $width
     * @param mixed $style
     * @return \PhpOffice\PhpWord\Element\Cell
     */
    public function addCell($width = 1, $style = null)
    {
        $index = count($this->rows) - 1;
        $row = $this->rows[$index];
        $cell = $row->addCell($width, $style);

        return $cell;
    }

    /**
     * Get all rows
     *
     * @return \PhpOffice\PhpWord\Element\Row[]
     */
    public function getRows()
    {
        return $this->rows;
    }

    /**
     * Get table style
     *
     * @return \PhpOffice\PhpWord\Style\Table
     */
    public function getStyle()
    {
        return $this->style;
    }

    /**
     * Get table width
     *
     * @return int
     */
    public function getWidth()
    {
        return $this->width;
    }

    /**
     * Set table width.
     *
     * @param int $width
     */
    public function setWidth($width)
    {
        $this->width = $width;
    }

    /**
     * Get column count
     *
     * @return int
     */
    public function countColumns()
    {
        $columnCount = 0;

        $rowCount = count($this->rows);
        for ($i = 0; $i < $rowCount; $i++) {
            /** @var \PhpOffice\PhpWord\Element\Row $row Type hint */
            $row = $this->rows[$i];
            $cellCount = count($row->getCells());
            if ($columnCount < $cellCount) {
                $columnCount = $cellCount;
            }
        }

        return $columnCount;
    }

    /**
     * The first declared cell width for each column
     *
     * @return int[]
     */
    public function findFirstDefinedCellWidths()
    {
        $cellWidths = array();

        foreach ($this->rows as $row) {
            $cells = $row->getCells();
            if (count($cells) <= count($cellWidths)) {
                continue;
            }
            $cellWidths = array();
            foreach ($cells as $cell) {
                $cellWidths[] = $cell->getWidth();
            }
        }

        return $cellWidths;
    }
}

因为我们正在“覆盖”文件,所以我们不能重复使用扩展它的原始文件(至少,我不知道如何)。此处唯一的变化是 public function addCell($width = 1, $style = null) 始终将“1”作为函数的默认值。这解决了您的第二个问题,现在您可以在没有值的情况下调用 $table->addCell()

演示

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

$document = new \PhpOffice\PhpWord\PhpWord();

$section = $document->addSection();

// This list **does** get rendered in the ODT file
$section->addListItem('Employee ID: 98765');
$section->addListItem('First Name: John');
$section->addListItem('Last Name: Doe');
$section->addListItem('SSN: 12345');

$table = $section->addTable();
$table->addRow();
$table->addCell()->addText('SSN');
$table->addCell()->addText('Email');
$table->addCell()->addText('Phone No');

$table->addRow();
$table->addCell()->addText('12345');
$table->addCell()->addText('jd@example.com');
$table->addCell()->addText('999 1234');

// Save the document as DOCX
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($document, 'Word2007');
$objWriter->save('document.docx');

// Save the document as ODT
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($document, 'ODText');
$objWriter->save('document.odt');