426 lines
13 KiB
PHP
426 lines
13 KiB
PHP
<?php
|
|
namespace fox;
|
|
|
|
use Exception;
|
|
use DOMDocument;
|
|
use ZipArchive;
|
|
use DOMXPath;
|
|
|
|
/**
|
|
*
|
|
* Class fox\oasis
|
|
*
|
|
* @copyright MX STAR LLC 2021
|
|
* @version 4.0.0
|
|
* @author Pavel Dmitriev
|
|
* @license GPLv3
|
|
*
|
|
*/
|
|
class oasis
|
|
{
|
|
|
|
protected $srcFileName;
|
|
|
|
protected $srcFileType;
|
|
|
|
protected $newFileName;
|
|
|
|
protected $newFileIsTemporary = true;
|
|
|
|
protected $dom;
|
|
|
|
protected $dom2;
|
|
|
|
protected $tmpath = "/tmp";
|
|
|
|
protected $parent;
|
|
|
|
protected $parent_node;
|
|
|
|
protected $oldnode;
|
|
|
|
protected $odsParams = [];
|
|
|
|
public function __construct($srcFilePath = null, $tmpath = null)
|
|
{
|
|
if (! empty($tmpath)) {
|
|
$this->tmpath = $tmpath;
|
|
}
|
|
if (! empty($srcFilePath)) {
|
|
$this->loadODF($srcFilePath);
|
|
}
|
|
}
|
|
|
|
public function loadODF($srcFile)
|
|
{
|
|
$mimeType = file_get_contents("zip://" . $srcFile . "#mimetype");
|
|
if (! $mimeType) {
|
|
throw new Exception("Unable to determine file type of " . $srcFile);
|
|
}
|
|
|
|
switch ($mimeType) {
|
|
case "application/vnd.oasis.opendocument.text":
|
|
$this->srcFileType = 'odt';
|
|
break;
|
|
case "application/vnd.oasis.opendocument.spreadsheet":
|
|
$this->srcFileType = 'ods';
|
|
break;
|
|
default:
|
|
throw new Exception("Unknown file type: $mimeType");
|
|
}
|
|
|
|
$this->srcFileName = $srcFile;
|
|
|
|
$this->dom = new DOMDocument();
|
|
$this->dom->load("zip://" . $this->srcFileName . "#content.xml");
|
|
}
|
|
|
|
public function saveODF($newFileName = null)
|
|
{
|
|
if (empty($newFileName)) {
|
|
|
|
$uuid = common::getGUIDc();
|
|
|
|
if (file_exists($this->tmpath) && ! is_dir($this->tmpath)) {
|
|
throw new Exception("TMPatn not a directory!");
|
|
}
|
|
|
|
if (! file_exists($this->tmpath)) {
|
|
mkdir($this->tmpath);
|
|
}
|
|
|
|
if (file_exists($this->tmpath . "/" . $uuid . "." . $this->srcFileType)) {
|
|
$uuid = common::getGUIDc();
|
|
}
|
|
|
|
$newFileName = $this->tmpath . "/" . $uuid . "." . $this->srcFileType;
|
|
$this->newFileIsTemporary = true;
|
|
} else {
|
|
$this->newFileIsTemporary = false;
|
|
}
|
|
|
|
$this->newFileName = $newFileName;
|
|
copy($this->srcFileName, $this->newFileName);
|
|
|
|
$this->commit();
|
|
$xml = $this->dom->saveXML();
|
|
// file_put_contents("$tmpath/$guid/content.xml", $xml);
|
|
|
|
$zip = new ZipArchive();
|
|
|
|
$res = $zip->open($this->newFileName);
|
|
if ($res === TRUE) {
|
|
$zip->addFromString('content.xml', $xml);
|
|
$zip->close();
|
|
} else {
|
|
throw new Exception("Unable to save result");
|
|
}
|
|
|
|
return $newFileName;
|
|
}
|
|
|
|
public function export($type, $newFileName = null)
|
|
{
|
|
$ods = $this->saveODF();
|
|
return fileConverter::convert($ods, $newFileName, $type);
|
|
}
|
|
|
|
public function commit()
|
|
{
|
|
if ($this->srcFileType == 'ods') {
|
|
$this->odsCommitParams();
|
|
}
|
|
|
|
if (! empty($this->parent) && ! empty($this->parent->documentElement)) {
|
|
// Импортируем созданый ранее элемент в текущее дерево
|
|
$newnode = $this->dom->importNode($this->parent->documentElement, true);
|
|
|
|
$this->oldnode->parentNode->replaceChild($newnode, $this->oldnode);
|
|
|
|
$this->parent = null;
|
|
$this->oldnode = null;
|
|
$this->parent_node = null;
|
|
$this->dom2 = null;
|
|
}
|
|
}
|
|
|
|
public function odsCommitParams($erase_notfound_params = true)
|
|
{
|
|
if ($this->srcFileType != 'ods') {
|
|
return false;
|
|
}
|
|
if (empty($this->odsParams)) {
|
|
return true;
|
|
}
|
|
|
|
$xpath = new DOMXpath($this->dom);
|
|
$nodelist = $xpath->query("/office:document-content/office:body/office:spreadsheet/table:table");
|
|
$this->oldnode = $nodelist->item(0);
|
|
|
|
$this->parent = new DomDocument();
|
|
$this->parent_node = $this->parent->importNode($this->oldnode);
|
|
|
|
foreach ($this->oldnode->childNodes as $t_node) {
|
|
if ($t_node->nodeName == 'table:table-row') {
|
|
foreach ($t_node->getElementsByTagname("*") as $t2_node) {
|
|
$res = null;
|
|
if ($t2_node->nodeName == 'text:p' && preg_match("/^<t:(.*)>$/", $t2_node->nodeValue, $res)) {
|
|
if ((array_key_exists($res[1], $this->odsParams))) {
|
|
|
|
$this->odsUpdateCellValue($this->odsParams[$res[1]], $t2_node->parentNode);
|
|
} elseif ($erase_notfound_params) {
|
|
$this->odsUpdateCellValue(null, $t2_node->parentNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$this->odsParams = null;
|
|
}
|
|
|
|
public function addParam($paramName, $paramValue)
|
|
{
|
|
if ($this->srcFileType == 'odt') {
|
|
|
|
if (is_null($this->parent)) {
|
|
$this->prepareODFParam();
|
|
}
|
|
// Создаем дочерний элемент в структуре
|
|
$child_node = $this->parent->createElement('text:user-field-decl');
|
|
|
|
$attribute = $this->parent->createAttribute("office:value-type");
|
|
$attribute->value = "string";
|
|
$child_node->appendChild($attribute);
|
|
|
|
$attribute = $this->parent->createAttribute("office:string-value");
|
|
$attribute->value = $paramValue;
|
|
$child_node->appendChild($attribute);
|
|
|
|
$attribute = $this->parent->createAttribute("text:name");
|
|
$attribute->value = $paramName;
|
|
$child_node->appendChild($attribute);
|
|
|
|
$this->parent_node->appendChild($child_node);
|
|
|
|
$this->parent->appendChild($this->parent_node);
|
|
// закончили создавать дочерний элемент
|
|
} elseif ($this->srcFileType == 'ods') {
|
|
if (empty($this->odsParams)) {
|
|
$this->odsParams = [];
|
|
}
|
|
;
|
|
$this->odsParams[$paramName] = $paramValue;
|
|
}
|
|
}
|
|
|
|
public function odtTableRowAdd($tag, $row_count)
|
|
{
|
|
if ($this->srcFileType != 'odt') {
|
|
return false;
|
|
}
|
|
if (is_null($this->dom2)) {
|
|
$this->odtPrepareTableRowAdd();
|
|
}
|
|
|
|
$nodelist = $this->dom->getElementsByTagname("user-field-get");
|
|
$table_row_node = null;
|
|
|
|
foreach ($nodelist as $node) {
|
|
|
|
if (! is_null($table_row_node)) {
|
|
break;
|
|
}
|
|
;
|
|
|
|
$tag_class = explode('.', $node->getAttribute('text:name'), 2)[0];
|
|
|
|
if ($tag_class == $tag) {
|
|
$n = $node;
|
|
while (($n = $n->parentNode) && (is_null($table_row_node))) {
|
|
if ($n->nodeName == 'table:table-row') {
|
|
$table_row_node = $n;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_null($table_row_node)) {
|
|
return - 1;
|
|
}
|
|
|
|
$table_node = $table_row_node->parentNode;
|
|
|
|
$element = $this->dom2->importNode($table_node, false);
|
|
|
|
foreach ($table_node->childNodes as $node) {
|
|
$nn = $this->dom2->importNode($node, true);
|
|
|
|
$nodelistS = $node->getElementsByTagname("user-field-get");
|
|
|
|
$nodeV = false;
|
|
|
|
foreach ($nodelistS as $nodeS) {
|
|
$tag_class = explode('.', $nodeS->getAttribute('text:name'), 2)[0];
|
|
|
|
if ($tag_class == $tag) {
|
|
$nodeV = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($nodeV) {
|
|
// print "NODEV ".$nodeS->getAttribute('text:name')."\n";
|
|
for ($i = 0; $i < $row_count; $i ++) {
|
|
$element->appendChild($this->odtTableNodeAddIdx($table_row_node, $tag, $i));
|
|
}
|
|
} else {
|
|
// print "General Node\n";
|
|
$element->appendChild($nn);
|
|
}
|
|
}
|
|
|
|
$newnode = $this->dom->importNode($element, true);
|
|
$table_node->parentNode->replaceChild($newnode, $table_node);
|
|
return 1;
|
|
}
|
|
|
|
public function odsInsertData($arr, $marker = '<d:start>')
|
|
{
|
|
if ($this->srcFileType != 'ods') {
|
|
return false;
|
|
}
|
|
if (empty($this->parent)) {
|
|
$xpath = new DOMXpath($this->dom);
|
|
$nodelist = $xpath->query("/office:document-content/office:body/office:spreadsheet/table:table");
|
|
$this->oldnode = $nodelist->item(0);
|
|
|
|
$this->parent = new DomDocument();
|
|
// $this->parent_node = $this->parent->createElement('table:table');
|
|
$this->parent_node = $this->parent->importNode($this->oldnode);
|
|
}
|
|
|
|
$marker_found = false;
|
|
foreach ($this->oldnode->childNodes as $t_node) {
|
|
if (! $marker_found && $t_node->nodeName == 'table:table-row') {
|
|
foreach ($t_node->getElementsByTagname("*") as $t2_node) {
|
|
if ($t2_node->nodeName == 'text:p' && $t2_node->nodeValue == $marker) {
|
|
$marker_found = true;
|
|
$t2_row = $t2_node->parentNode->parentNode;
|
|
for ($i = 0; $i < count($arr); $i ++) {
|
|
$v_arr = $arr[$i];
|
|
$v_idx = 0;
|
|
foreach ($t2_row->childNodes as $t2_cell) {
|
|
$v_idx ++;
|
|
if ($v_idx - 1 > array_key_last($v_arr)) {
|
|
$val = null;
|
|
} elseif (array_key_exists($v_idx - 1, $v_arr)) {
|
|
$val = $v_arr[$v_idx - 1];
|
|
} else {
|
|
$val = null;
|
|
}
|
|
|
|
$this->odsUpdateCellValue($val, $t2_cell);
|
|
}
|
|
$this->parent_node->appendChild($this->parent->importNode($t_node, true));
|
|
}
|
|
continue (2);
|
|
}
|
|
}
|
|
}
|
|
$this->parent_node->appendChild($this->parent->importNode($t_node, true));
|
|
}
|
|
$this->parent->appendChild($this->parent_node);
|
|
}
|
|
|
|
protected function odsUpdateCellValue($val, $t2_cell)
|
|
{
|
|
switch (gettype($val)) {
|
|
case "string":
|
|
$val_type = 'string';
|
|
break;
|
|
case "integer":
|
|
$val_type = 'float';
|
|
break;
|
|
case "float":
|
|
$val_type = 'float';
|
|
break;
|
|
case "double":
|
|
$val_type = 'float';
|
|
break;
|
|
case "NULL":
|
|
$val_type = 'null';
|
|
break;
|
|
default:
|
|
$val_type = 'string';
|
|
break;
|
|
}
|
|
|
|
foreach ($t2_cell->childNodes as $t2_child) {
|
|
$t2_cell->removeChild($t2_child);
|
|
}
|
|
|
|
switch ($val_type) {
|
|
case "string":
|
|
$t2_cell->setAttribute("office:value-type", $val_type);
|
|
$t2_cell->setAttribute("calcext:value-type", $val_type);
|
|
$t2_cell->appendChild($this->dom->createElement('text:p', $val));
|
|
break;
|
|
|
|
case "null":
|
|
$t2_cell->removeAttribute("office:value-type");
|
|
$t2_cell->removeAttribute("calcext:value-type");
|
|
$t2_cell->removeAttribute("office:value");
|
|
break;
|
|
|
|
case "float":
|
|
$t2_cell->setAttribute("office:value-type", $val_type);
|
|
$t2_cell->setAttribute("calcext:value-type", $val_type);
|
|
$t2_cell->setAttribute("office:value", $val);
|
|
break;
|
|
}
|
|
}
|
|
|
|
protected function prepareODFParam()
|
|
{
|
|
$xpath = new DOMXpath($this->dom);
|
|
$nodelist = $xpath->query("/office:document-content/office:body/office:text/text:user-field-decls");
|
|
|
|
$this->oldnode = $nodelist->item(0);
|
|
|
|
$this->parent = new DomDocument();
|
|
$this->parent_node = $this->parent->createElement('text:user-field-decls');
|
|
}
|
|
|
|
protected function odtPrepareTableRowAdd()
|
|
{
|
|
$this->dom2 = new DomDocument();
|
|
}
|
|
|
|
protected function odtTableNodeAddIdx($row_node, $key, $idx)
|
|
{
|
|
$d2node = $this->dom2->importNode($row_node, true);
|
|
|
|
$nodelist = $d2node->getElementsByTagname("user-field-get");
|
|
|
|
foreach ($nodelist as $node) {
|
|
|
|
$tag_class = explode('.', $node->getAttribute('text:name'), 2)[0];
|
|
|
|
if ($tag_class == $key) {
|
|
$node->setAttribute('text:name', $node->getAttribute('text:name') . $idx);
|
|
}
|
|
}
|
|
|
|
return $d2node;
|
|
}
|
|
|
|
public function __destruct()
|
|
{
|
|
if ($this->newFileIsTemporary && file_exists($this->newFileName)) {
|
|
unlink($this->newFileName);
|
|
}
|
|
}
|
|
}
|
|
|
|
?>
|