解析ODT文件时,带有XML标记的变量出错

时间:2015-09-28 16:47:27

标签: php odt opentbs tinybutstrong

我正在一个需要处理ODT文档的系统中实现TinyButStrong / OpenTBS,并且我遇到了一个特定模板的问题,该模板在变量名称中有标记。

情况如下:

模板部分:

enter image description here

content.xml

的相关部分
<table:table-cell table:style-name="Table3.A1" office:value-type="string">
  <text:p text:style-name="P22">Tipo de documento</text:p>
  <text:p text:style-name="P29">
    <text:span text:style-name="T7">
     [b.</text:span>tipoDocumento<text:span text:style-name="T7">]
    </text:span>
  </text:p>
</table:table-cell>

如您所见,变量名称为</text:span>tipoDocumento<text:span text:style-name="T7">。该文档是在LibreOffice中编辑的,由于某些未知原因,标签已被添加。

我以为我可以传递完整的变量名(包含标签),OpenTBS会正确解析值,所以我尝试了以下内容:

$data = ['</text:span>tipoDocumento<text:span text:style-name="T7">' => 'somevalue'];
$tbs = new clsTinyButStrong;
$tbs->Plugin(TBS_INSTALL, OPENTBS_PLUGIN);
$tbs->LoadTemplate($templatePath, OPENTBS_ALREADY_UTF8);
// Note that we need to send an array of arrays to $data,
$tbs->MergeBlock($block, 'array', [$data]);

但这会导致TBS错误:

  

<b>TinyButStrong Error</b> in field &#91;b.</text:span>tipoDocumento<text:span text:style-name...]: item '&lt;/text:span&gt;tipoDocumento&lt;text:span text:style-name' is not an existing key in the array. <em>This message can be cancelled using parameter 'noerr'.</em>

我做了一些调试,并在核心tbs_class.php第1177行(meth_Locator_Replace()中,这是投掷错误的地方),$Loc->SubLst[$i]的内容是</text:span>tipoDocumento<text:span text:style-name,与我的数组中的值不匹配。

所以,我假设由于某种原因,TBS正在通过等号(=)爆炸索引,这导致了这个问题。所以,

  1. 这是故意的吗?
  2. 是否可以修复(如果有错误)以允许带有等号的标签?
  3. 有没有更好的方法来避免变量中的标记,或者在LibreOffice中是否有办法避免这种情况?

2 个答案:

答案 0 :(得分:1)

字符串</text:span>tipoDocumento<text:span text:style-name="T7">不能是TBS中字段的名称。这是因为空格,符号相等,点和分号,以及其他一些是TBS字段的特殊字符。

当您更改格式化或有拼写信息时,可以在LibreOffice(甚至是Ms Office)中自动添加此类内部XML内容。

解决方法是在LibreOffice中选择TBS字段,然后剪切它,然后粘贴而不格式化。然后所有内部XML都应该已经消失,或者至少它只是绑定了文本而没有删除它。

答案 1 :(得分:0)

@Skrol29的答案是最可靠的解决方案。

但是,我们使用模板的原因之一是让最终用户能够编辑它们,并且要解释它们为什么需要这样做是不容易的,因为LibreOffice中没有视觉上的变化(或者Microsoft Office,就此而言。)

因此,我在保存之前最终解析了模板源,从而从变量中删除了所有XML标记。

这是我上传新模板文件时使用的代码:

// Create a temporary file, only to load it with TBS
// $fileContents is the binary file contents and $extensao is the file extension
$filePath = intranet_storage_path(sha1($fileContents) . '.' . $extensao, 'tmp');
// Store the binary contents in the file path
file_put_contents($filePath, $fileContents);

// Create a new TBS instance and load OpenTBS
$tbs = new clsTinyButStrong;
$tbs->Plugin(TBS_INSTALL, OPENTBS_PLUGIN);

// Load the temporary file
$tbs->LoadTemplate($filePath, OPENTBS_ALREADY_UTF8);

// Find all variables (the only block name is 'b')
preg_match_all(
    "/(\[b\.  # Start by finding a part of [ followed by the block name and a dot
    [^.\];]+  # Now we should get all characters until one of the following is found: `.` (dot), `]`, `;
    [\]|;]    # Stop the regex when a `]` or `;` is found.
    )/ix",
    $tbs->Source,
    $matches
);

// Loop through all the found variables
$searched = $replaced = [];
foreach ($matches[0] as $var) {
    // Fill the $searched and $replaced where $searched is the real variable name 
    // with XML tags (if they exist) and $replaced is the variable without tags
    $searched[] = $var;
    $replaced[] = strip_tags($var);
}

// Replace the contents of the Source
$tbs->Source = str_replace($searched, $replaced, $tbs->Source);

// Store the final template file with variables without XML
$tbs->Show(OPENTBS_FILE, $filePath);

我必须声明,当变量中只有一个打开或关闭标记时,此解决方案将导致XML无效。以下示例将破坏XML(您将无法打开或解析文档):

<text:span text:style-name="T7">[b.tipoDocumento<text:span text:style-name="T7">]</text:span>
// OR
<text:span text:style-name="T7">[b.</text:span>tipoDocumento]</text:span>

但是,从我所拥有的测试用例中,总有一个开始和结束标记(如问题中所示),因此剥离它们将产生有效的XML。