ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Ods.php
Go to the documentation of this file.
1 <?php
2 
4 
5 use DateTime;
6 use DOMAttr;
7 use DOMDocument;
8 use DOMElement;
9 use DOMNode;
13 use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
25 use Throwable;
26 use XMLReader;
27 use ZipArchive;
28 
29 class Ods extends BaseReader
30 {
34  public function __construct()
35  {
36  parent::__construct();
37  $this->securityScanner = XmlScanner::getInstance($this);
38  }
39 
47  public function canRead($pFilename)
48  {
49  File::assertFile($pFilename);
50 
51  $mimeType = 'UNKNOWN';
52 
53  // Load file
54 
55  $zip = new ZipArchive();
56  if ($zip->open($pFilename) === true) {
57  // check if it is an OOXML archive
58  $stat = $zip->statName('mimetype');
59  if ($stat && ($stat['size'] <= 255)) {
60  $mimeType = $zip->getFromName($stat['name']);
61  } elseif ($zip->statName('META-INF/manifest.xml')) {
62  $xml = simplexml_load_string(
63  $this->securityScanner->scan($zip->getFromName('META-INF/manifest.xml')),
64  'SimpleXMLElement',
66  );
67  $namespacesContent = $xml->getNamespaces(true);
68  if (isset($namespacesContent['manifest'])) {
69  $manifest = $xml->children($namespacesContent['manifest']);
70  foreach ($manifest as $manifestDataSet) {
71  $manifestAttributes = $manifestDataSet->attributes($namespacesContent['manifest']);
72  if ($manifestAttributes->{'full-path'} == '/') {
73  $mimeType = (string) $manifestAttributes->{'media-type'};
74 
75  break;
76  }
77  }
78  }
79  }
80 
81  $zip->close();
82  }
83 
84  return $mimeType === 'application/vnd.oasis.opendocument.spreadsheet';
85  }
86 
94  public function listWorksheetNames($pFilename)
95  {
96  File::assertFile($pFilename);
97 
98  $zip = new ZipArchive();
99  if ($zip->open($pFilename) !== true) {
100  throw new ReaderException('Could not open ' . $pFilename . ' for reading! Error opening file.');
101  }
102 
103  $worksheetNames = [];
104 
105  $xml = new XMLReader();
106  $xml->xml(
107  $this->securityScanner->scanFile('zip://' . realpath($pFilename) . '#content.xml'),
108  null,
110  );
111  $xml->setParserProperty(2, true);
112 
113  // Step into the first level of content of the XML
114  $xml->read();
115  while ($xml->read()) {
116  // Quickly jump through to the office:body node
117  while ($xml->name !== 'office:body') {
118  if ($xml->isEmptyElement) {
119  $xml->read();
120  } else {
121  $xml->next();
122  }
123  }
124  // Now read each node until we find our first table:table node
125  while ($xml->read()) {
126  if ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) {
127  // Loop through each table:table node reading the table:name attribute for each worksheet name
128  do {
129  $worksheetNames[] = $xml->getAttribute('table:name');
130  $xml->next();
131  } while ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT);
132  }
133  }
134  }
135 
136  return $worksheetNames;
137  }
138 
146  public function listWorksheetInfo($pFilename)
147  {
148  File::assertFile($pFilename);
149 
150  $worksheetInfo = [];
151 
152  $zip = new ZipArchive();
153  if ($zip->open($pFilename) !== true) {
154  throw new ReaderException('Could not open ' . $pFilename . ' for reading! Error opening file.');
155  }
156 
157  $xml = new XMLReader();
158  $xml->xml(
159  $this->securityScanner->scanFile('zip://' . realpath($pFilename) . '#content.xml'),
160  null,
162  );
163  $xml->setParserProperty(2, true);
164 
165  // Step into the first level of content of the XML
166  $xml->read();
167  while ($xml->read()) {
168  // Quickly jump through to the office:body node
169  while ($xml->name !== 'office:body') {
170  if ($xml->isEmptyElement) {
171  $xml->read();
172  } else {
173  $xml->next();
174  }
175  }
176  // Now read each node until we find our first table:table node
177  while ($xml->read()) {
178  if ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) {
179  $worksheetNames[] = $xml->getAttribute('table:name');
180 
181  $tmpInfo = [
182  'worksheetName' => $xml->getAttribute('table:name'),
183  'lastColumnLetter' => 'A',
184  'lastColumnIndex' => 0,
185  'totalRows' => 0,
186  'totalColumns' => 0,
187  ];
188 
189  // Loop through each child node of the table:table element reading
190  $currCells = 0;
191  do {
192  $xml->read();
193  if ($xml->name == 'table:table-row' && $xml->nodeType == XMLReader::ELEMENT) {
194  $rowspan = $xml->getAttribute('table:number-rows-repeated');
195  $rowspan = empty($rowspan) ? 1 : $rowspan;
196  $tmpInfo['totalRows'] += $rowspan;
197  $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
198  $currCells = 0;
199  // Step into the row
200  $xml->read();
201  do {
202  $doread = true;
203  if ($xml->name == 'table:table-cell' && $xml->nodeType == XMLReader::ELEMENT) {
204  if (!$xml->isEmptyElement) {
205  ++$currCells;
206  $xml->next();
207  $doread = false;
208  }
209  } elseif ($xml->name == 'table:covered-table-cell' && $xml->nodeType == XMLReader::ELEMENT) {
210  $mergeSize = $xml->getAttribute('table:number-columns-repeated');
211  $currCells += (int) $mergeSize;
212  }
213  if ($doread) {
214  $xml->read();
215  }
216  } while ($xml->name != 'table:table-row');
217  }
218  } while ($xml->name != 'table:table');
219 
220  $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
221  $tmpInfo['lastColumnIndex'] = $tmpInfo['totalColumns'] - 1;
222  $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
223  $worksheetInfo[] = $tmpInfo;
224  }
225  }
226  }
227 
228  return $worksheetInfo;
229  }
230 
238  public function load($pFilename)
239  {
240  // Create new Spreadsheet
241  $spreadsheet = new Spreadsheet();
242 
243  // Load into this instance
244  return $this->loadIntoExisting($pFilename, $spreadsheet);
245  }
246 
254  public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
255  {
256  File::assertFile($pFilename);
257 
258  $zip = new ZipArchive();
259  if ($zip->open($pFilename) !== true) {
260  throw new Exception("Could not open {$pFilename} for reading! Error opening file.");
261  }
262 
263  // Meta
264 
265  $xml = @simplexml_load_string(
266  $this->securityScanner->scan($zip->getFromName('meta.xml')),
267  'SimpleXMLElement',
269  );
270  if ($xml === false) {
271  throw new Exception('Unable to read data from {$pFilename}');
272  }
273 
274  $namespacesMeta = $xml->getNamespaces(true);
275 
276  (new DocumentProperties($spreadsheet))->load($xml, $namespacesMeta);
277 
278  // Styles
279 
280  $dom = new DOMDocument('1.01', 'UTF-8');
281  $dom->loadXML(
282  $this->securityScanner->scan($zip->getFromName('styles.xml')),
284  );
285 
286  $pageSettings = new PageSettings($dom);
287 
288  // Main Content
289 
290  $dom = new DOMDocument('1.01', 'UTF-8');
291  $dom->loadXML(
292  $this->securityScanner->scan($zip->getFromName('content.xml')),
294  );
295 
296  $officeNs = $dom->lookupNamespaceUri('office');
297  $tableNs = $dom->lookupNamespaceUri('table');
298  $textNs = $dom->lookupNamespaceUri('text');
299  $xlinkNs = $dom->lookupNamespaceUri('xlink');
300 
301  $pageSettings->readStyleCrossReferences($dom);
302 
303  $autoFilterReader = new AutoFilter($spreadsheet, $tableNs);
304  $definedNameReader = new DefinedNames($spreadsheet, $tableNs);
305 
306  // Content
307  $spreadsheets = $dom->getElementsByTagNameNS($officeNs, 'body')
308  ->item(0)
309  ->getElementsByTagNameNS($officeNs, 'spreadsheet');
310 
311  foreach ($spreadsheets as $workbookData) {
313  $tables = $workbookData->getElementsByTagNameNS($tableNs, 'table');
314 
315  $worksheetID = 0;
316  foreach ($tables as $worksheetDataSet) {
318  $worksheetName = $worksheetDataSet->getAttributeNS($tableNs, 'name');
319 
320  // Check loadSheetsOnly
321  if (
322  isset($this->loadSheetsOnly)
323  && $worksheetName
324  && !in_array($worksheetName, $this->loadSheetsOnly)
325  ) {
326  continue;
327  }
328 
329  $worksheetStyleName = $worksheetDataSet->getAttributeNS($tableNs, 'style-name');
330 
331  // Create sheet
332  if ($worksheetID > 0) {
333  $spreadsheet->createSheet(); // First sheet is added by default
334  }
335  $spreadsheet->setActiveSheetIndex($worksheetID);
336 
337  if ($worksheetName) {
338  // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in
339  // formula cells... during the load, all formulae should be correct, and we're simply
340  // bringing the worksheet name in line with the formula, not the reverse
341  $spreadsheet->getActiveSheet()->setTitle((string) $worksheetName, false, false);
342  }
343 
344  // Go through every child of table element
345  $rowID = 1;
346  foreach ($worksheetDataSet->childNodes as $childNode) {
349  // Filter elements which are not under the "table" ns
350  if ($childNode->namespaceURI != $tableNs) {
351  continue;
352  }
353 
354  $key = $childNode->nodeName;
355 
356  // Remove ns from node name
357  if (strpos($key, ':') !== false) {
358  $keyChunks = explode(':', $key);
359  $key = array_pop($keyChunks);
360  }
361 
362  switch ($key) {
363  case 'table-header-rows':
365  // ($rowData it's not used at all and I'm not sure that PHPExcel
366  // has an API for this)
367 
368 // foreach ($rowData as $keyRowData => $cellData) {
369 // $rowData = $cellData;
370 // break;
371 // }
372  break;
373  case 'table-row':
374  if ($childNode->hasAttributeNS($tableNs, 'number-rows-repeated')) {
375  $rowRepeats = $childNode->getAttributeNS($tableNs, 'number-rows-repeated');
376  } else {
377  $rowRepeats = 1;
378  }
379 
380  $columnID = 'A';
382  foreach ($childNode->childNodes as $cellData) {
383  if ($this->getReadFilter() !== null) {
384  if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
385  ++$columnID;
386 
387  continue;
388  }
389  }
390 
391  // Initialize variables
392  $formatting = $hyperlink = null;
393  $hasCalculatedValue = false;
394  $cellDataFormula = '';
395 
396  if ($cellData->hasAttributeNS($tableNs, 'formula')) {
397  $cellDataFormula = $cellData->getAttributeNS($tableNs, 'formula');
398  $hasCalculatedValue = true;
399  }
400 
401  // Annotations
402  $annotation = $cellData->getElementsByTagNameNS($officeNs, 'annotation');
403 
404  if ($annotation->length > 0) {
405  $textNode = $annotation->item(0)->getElementsByTagNameNS($textNs, 'p');
406 
407  if ($textNode->length > 0) {
408  $text = $this->scanElementForText($textNode->item(0));
409 
410  $spreadsheet->getActiveSheet()
411  ->getComment($columnID . $rowID)
412  ->setText($this->parseRichText($text));
413 // ->setAuthor( $author )
414  }
415  }
416 
417  // Content
418 
420  $paragraphs = [];
421 
422  foreach ($cellData->childNodes as $item) {
425  // Filter text:p elements
426  if ($item->nodeName == 'text:p') {
427  $paragraphs[] = $item;
428  }
429  }
430 
431  if (count($paragraphs) > 0) {
432  // Consolidate if there are multiple p records (maybe with spans as well)
433  $dataArray = [];
434 
435  // Text can have multiple text:p and within those, multiple text:span.
436  // text:p newlines, but text:span does not.
437  // Also, here we assume there is no text data is span fields are specified, since
438  // we have no way of knowing proper positioning anyway.
439 
440  foreach ($paragraphs as $pData) {
441  $dataArray[] = $this->scanElementForText($pData);
442  }
443  $allCellDataText = implode("\n", $dataArray);
444 
445  $type = $cellData->getAttributeNS($officeNs, 'value-type');
446 
447  switch ($type) {
448  case 'string':
450  $dataValue = $allCellDataText;
451 
452  foreach ($paragraphs as $paragraph) {
453  $link = $paragraph->getElementsByTagNameNS($textNs, 'a');
454  if ($link->length > 0) {
455  $hyperlink = $link->item(0)->getAttributeNS($xlinkNs, 'href');
456  }
457  }
458 
459  break;
460  case 'boolean':
462  $dataValue = ($allCellDataText == 'TRUE') ? true : false;
463 
464  break;
465  case 'percentage':
467  $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
468 
469  // percentage should always be float
470  //if (floor($dataValue) == $dataValue) {
471  // $dataValue = (int) $dataValue;
472  //}
474 
475  break;
476  case 'currency':
478  $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
479 
480  if (floor($dataValue) == $dataValue) {
481  $dataValue = (int) $dataValue;
482  }
484 
485  break;
486  case 'float':
488  $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
489 
490  if (floor($dataValue) == $dataValue) {
491  if ($dataValue == (int) $dataValue) {
492  $dataValue = (int) $dataValue;
493  }
494  }
495 
496  break;
497  case 'date':
499  $value = $cellData->getAttributeNS($officeNs, 'date-value');
500 
501  $dateObj = new DateTime($value);
502  [$year, $month, $day, $hour, $minute, $second] = explode(
503  ' ',
504  $dateObj->format('Y m d H i s')
505  );
506 
507  $dataValue = Date::formattedPHPToExcel(
508  (int) $year,
509  (int) $month,
510  (int) $day,
511  (int) $hour,
512  (int) $minute,
513  (int) $second
514  );
515 
516  if ($dataValue != floor($dataValue)) {
518  . ' '
520  } else {
521  $formatting = NumberFormat::FORMAT_DATE_XLSX15;
522  }
523 
524  break;
525  case 'time':
527 
528  $timeValue = $cellData->getAttributeNS($officeNs, 'time-value');
529 
530  $dataValue = Date::PHPToExcel(
531  strtotime(
532  '01-01-1970 ' . implode(':', sscanf($timeValue, 'PT%dH%dM%dS'))
533  )
534  );
535  $formatting = NumberFormat::FORMAT_DATE_TIME4;
536 
537  break;
538  default:
539  $dataValue = null;
540  }
541  } else {
543  $dataValue = null;
544  }
545 
546  if ($hasCalculatedValue) {
548  $cellDataFormula = substr($cellDataFormula, strpos($cellDataFormula, ':=') + 1);
549  $cellDataFormula = $this->convertToExcelFormulaValue($cellDataFormula);
550  }
551 
552  if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) {
553  $colRepeats = (int) $cellData->getAttributeNS($tableNs, 'number-columns-repeated');
554  } else {
555  $colRepeats = 1;
556  }
557 
558  if ($type !== null) {
559  for ($i = 0; $i < $colRepeats; ++$i) {
560  if ($i > 0) {
561  ++$columnID;
562  }
563 
564  if ($type !== DataType::TYPE_NULL) {
565  for ($rowAdjust = 0; $rowAdjust < $rowRepeats; ++$rowAdjust) {
566  $rID = $rowID + $rowAdjust;
567 
568  $cell = $spreadsheet->getActiveSheet()
569  ->getCell($columnID . $rID);
570 
571  // Set value
572  if ($hasCalculatedValue) {
573  $cell->setValueExplicit($cellDataFormula, $type);
574  } else {
575  $cell->setValueExplicit($dataValue, $type);
576  }
577 
578  if ($hasCalculatedValue) {
579  $cell->setCalculatedValue($dataValue);
580  }
581 
582  // Set other properties
583  if ($formatting !== null) {
584  $spreadsheet->getActiveSheet()
585  ->getStyle($columnID . $rID)
586  ->getNumberFormat()
587  ->setFormatCode($formatting);
588  } else {
589  $spreadsheet->getActiveSheet()
590  ->getStyle($columnID . $rID)
591  ->getNumberFormat()
592  ->setFormatCode(NumberFormat::FORMAT_GENERAL);
593  }
594 
595  if ($hyperlink !== null) {
596  $cell->getHyperlink()
597  ->setUrl($hyperlink);
598  }
599  }
600  }
601  }
602  }
603 
604  // Merged cells
605  if (
606  $cellData->hasAttributeNS($tableNs, 'number-columns-spanned')
607  || $cellData->hasAttributeNS($tableNs, 'number-rows-spanned')
608  ) {
610  $columnTo = $columnID;
611 
612  if ($cellData->hasAttributeNS($tableNs, 'number-columns-spanned')) {
613  $columnIndex = Coordinate::columnIndexFromString($columnID);
614  $columnIndex += (int) $cellData->getAttributeNS($tableNs, 'number-columns-spanned');
615  $columnIndex -= 2;
616 
617  $columnTo = Coordinate::stringFromColumnIndex($columnIndex + 1);
618  }
619 
620  $rowTo = $rowID;
621 
622  if ($cellData->hasAttributeNS($tableNs, 'number-rows-spanned')) {
623  $rowTo = $rowTo + (int) $cellData->getAttributeNS($tableNs, 'number-rows-spanned') - 1;
624  }
625 
626  $cellRange = $columnID . $rowID . ':' . $columnTo . $rowTo;
627  $spreadsheet->getActiveSheet()->mergeCells($cellRange);
628  }
629  }
630 
631  ++$columnID;
632  }
633  $rowID += $rowRepeats;
634 
635  break;
636  }
637  }
638  $pageSettings->setPrintSettingsForWorksheet($spreadsheet->getActiveSheet(), $worksheetStyleName);
639  ++$worksheetID;
640  }
641 
642  $autoFilterReader->read($workbookData);
643  $definedNameReader->read($workbookData);
644  }
645  $spreadsheet->setActiveSheetIndex(0);
646 
647  if ($zip->locateName('settings.xml') !== false) {
648  $this->processSettings($zip, $spreadsheet);
649  }
650  // Return
651  return $spreadsheet;
652  }
653 
654  private function processSettings(ZipArchive $zip, Spreadsheet $spreadsheet): void
655  {
656  $dom = new DOMDocument('1.01', 'UTF-8');
657  $dom->loadXML(
658  $this->securityScanner->scan($zip->getFromName('settings.xml')),
660  );
661  //$xlinkNs = $dom->lookupNamespaceUri('xlink');
662  $configNs = $dom->lookupNamespaceUri('config');
663  //$oooNs = $dom->lookupNamespaceUri('ooo');
664  $officeNs = $dom->lookupNamespaceUri('office');
665  $settings = $dom->getElementsByTagNameNS($officeNs, 'settings')
666  ->item(0);
667  $this->lookForActiveSheet($settings, $spreadsheet, $configNs);
668  $this->lookForSelectedCells($settings, $spreadsheet, $configNs);
669  }
670 
671  private function lookForActiveSheet(DOMElement $settings, Spreadsheet $spreadsheet, string $configNs): void
672  {
674  foreach ($settings->getElementsByTagNameNS($configNs, 'config-item') as $t) {
675  if ($t->getAttributeNs($configNs, 'name') === 'ActiveTable') {
676  try {
677  $spreadsheet->setActiveSheetIndexByName($t->nodeValue);
678  } catch (Throwable $e) {
679  // do nothing
680  }
681 
682  break;
683  }
684  }
685  }
686 
687  private function lookForSelectedCells(DOMElement $settings, Spreadsheet $spreadsheet, string $configNs): void
688  {
690  foreach ($settings->getElementsByTagNameNS($configNs, 'config-item-map-named') as $t) {
691  if ($t->getAttributeNs($configNs, 'name') === 'Tables') {
692  foreach ($t->getElementsByTagNameNS($configNs, 'config-item-map-entry') as $ws) {
693  $setRow = $setCol = '';
694  $wsname = $ws->getAttributeNs($configNs, 'name');
695  foreach ($ws->getElementsByTagNameNS($configNs, 'config-item') as $configItem) {
696  $attrName = $configItem->getAttributeNs($configNs, 'name');
697  if ($attrName === 'CursorPositionX') {
698  $setCol = $configItem->nodeValue;
699  }
700  if ($attrName === 'CursorPositionY') {
701  $setRow = $configItem->nodeValue;
702  }
703  }
704  $this->setSelected($spreadsheet, $wsname, $setCol, $setRow);
705  }
706 
707  break;
708  }
709  }
710  }
711 
712  private function setSelected(Spreadsheet $spreadsheet, string $wsname, string $setCol, string $setRow): void
713  {
714  if (is_numeric($setCol) && is_numeric($setRow)) {
715  try {
716  $spreadsheet->getSheetByName($wsname)->setSelectedCellByColumnAndRow($setCol + 1, $setRow + 1);
717  } catch (Throwable $e) {
718  // do nothing
719  }
720  }
721  }
722 
728  protected function scanElementForText(DOMNode $element)
729  {
730  $str = '';
731  foreach ($element->childNodes as $child) {
733  if ($child->nodeType == XML_TEXT_NODE) {
734  $str .= $child->nodeValue;
735  } elseif ($child->nodeType == XML_ELEMENT_NODE && $child->nodeName == 'text:s') {
736  // It's a space
737 
738  // Multiple spaces?
740  $cAttr = $child->attributes->getNamedItem('c');
741  if ($cAttr) {
742  $multiplier = (int) $cAttr->nodeValue;
743  } else {
744  $multiplier = 1;
745  }
746 
747  $str .= str_repeat(' ', $multiplier);
748  }
749 
750  if ($child->hasChildNodes()) {
751  $str .= $this->scanElementForText($child);
752  }
753  }
754 
755  return $str;
756  }
757 
763  private function parseRichText($is)
764  {
765  $value = new RichText();
766  $value->createText($is);
767 
768  return $value;
769  }
770 
771  private function convertToExcelFormulaValue(string $openOfficeFormula): string
772  {
773  $temp = explode('"', $openOfficeFormula);
774  $tKey = false;
775  foreach ($temp as &$value) {
776  // Only replace in alternate array entries (i.e. non-quoted blocks)
777  if ($tKey = !$tKey) {
778  // Cell range reference in another sheet
779  $value = preg_replace('/\[\$?([^\.]+)\.([^\.]+):\.([^\.]+)\]/miu', '$1!$2:$3', $value);
780  // Cell reference in another sheet
781  $value = preg_replace('/\[\$?([^\.]+)\.([^\.]+)\]/miu', '$1!$2', $value ?? '');
782  // Cell range reference
783  $value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/miu', '$1:$2', $value ?? '');
784  // Simple cell reference
785  $value = preg_replace('/\[\.([^\.]+)\]/miu', '$1', $value ?? '');
786  // Convert references to defined names/formulae
787  $value = str_replace('$$', '', $value ?? '');
788 
789  $value = Calculation::translateSeparator(';', ',', $value, $inBraces);
790  }
791  }
792 
793  // Then rebuild the formula string
794  $excelFormula = implode('"', $temp);
795 
796  return $excelFormula;
797  }
798 }
getSheetByName($pName)
Get sheet by name.
$type
listWorksheetNames($pFilename)
Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object...
Definition: Ods.php:94
setActiveSheetIndexByName($pValue)
Set active sheet index by name.
__construct()
Create a new Ods Reader instance.
Definition: Ods.php:34
convertToExcelFormulaValue(string $openOfficeFormula)
Definition: Ods.php:771
static formattedPHPToExcel($year, $month, $day, $hours=0, $minutes=0, $seconds=0)
formattedPHPToExcel.
Definition: Date.php:291
listWorksheetInfo($pFilename)
Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns)...
Definition: Ods.php:146
static PHPToExcel($dateValue)
Convert a date from PHP to an MS Excel serialized date/time value.
Definition: Date.php:231
createSheet($sheetIndex=null)
Create sheet and add it to this workbook.
setActiveSheetIndex($pIndex)
Set active sheet index.
$text
Definition: errorreport.php:18
processSettings(ZipArchive $zip, Spreadsheet $spreadsheet)
Definition: Ods.php:654
static getInstance(Reader\IReader $reader)
Definition: XmlScanner.php:39
canRead($pFilename)
Can the current IReader read the file?
Definition: Ods.php:47
setSelected(Spreadsheet $spreadsheet, string $wsname, string $setCol, string $setRow)
Definition: Ods.php:712
load($pFilename)
Loads PhpSpreadsheet from file.
Definition: Ods.php:238
static getLibXmlLoaderOptions()
Get default options for libxml loader.
Definition: Settings.php:116
$i
Definition: disco.tpl.php:19
static translateSeparator($fromSeparator, $toSeparator, $formula, &$inBraces)
static assertFile($filename)
Assert that given path is an existing file and is readable, otherwise throw exception.
Definition: File.php:143
static columnIndexFromString($pString)
Column index from string.
Definition: Coordinate.php:265
$key
Definition: croninfo.php:18
static stringFromColumnIndex($columnIndex)
String from column index.
Definition: Coordinate.php:313