PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.0.3
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.0.3
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
visualizer / vendor / phpoffice / phpexcel / Classes / PHPExcel / ReferenceHelper.php

ReferenceHelper.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.0.3, at vendor/phpoffice/phpexcel/Classes/PHPExcel/ReferenceHelper.php

923 lines 39.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * PHPExcel
4 *
5 * Copyright (c) 2006 - 2014 PHPExcel
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 * @category PHPExcel
22 * @package PHPExcel
23 * @copyright Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
24 * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
25 * @version ##VERSION##, ##DATE##
26 */
27
28
29 /**
30 * PHPExcel_ReferenceHelper (Singleton)
31 *
32 * @category PHPExcel
33 * @package PHPExcel
34 * @copyright Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
35 */
36 class PHPExcel_ReferenceHelper
37 {
38 /** Constants */
39 /** Regular Expressions */
40 const REFHELPER_REGEXP_CELLREF = '((\w*|\'[^!]*\')!)?(?<![:a-z\$])(\$?[a-z]{1,3}\$?\d+)(?=[^:!\d\'])';
41 const REFHELPER_REGEXP_CELLRANGE = '((\w*|\'[^!]*\')!)?(\$?[a-z]{1,3}\$?\d+):(\$?[a-z]{1,3}\$?\d+)';
42 const REFHELPER_REGEXP_ROWRANGE = '((\w*|\'[^!]*\')!)?(\$?\d+):(\$?\d+)';
43 const REFHELPER_REGEXP_COLRANGE = '((\w*|\'[^!]*\')!)?(\$?[a-z]{1,3}):(\$?[a-z]{1,3})';
44
45 /**
46 * Instance of this class
47 *
48 * @var PHPExcel_ReferenceHelper
49 */
50 private static $_instance;
51
52 /**
53 * Get an instance of this class
54 *
55 * @return PHPExcel_ReferenceHelper
56 */
57 public static function getInstance() {
58 if (!isset(self::$_instance) || (self::$_instance === NULL)) {
59 self::$_instance = new PHPExcel_ReferenceHelper();
60 }
61
62 return self::$_instance;
63 }
64
65 /**
66 * Create a new PHPExcel_ReferenceHelper
67 */
68 protected function __construct() {
69 }
70
71 /**
72 * Compare two column addresses
73 * Intended for use as a Callback function for sorting column addresses by column
74 *
75 * @param string $a First column to test (e.g. 'AA')
76 * @param string $b Second column to test (e.g. 'Z')
77 * @return integer
78 */
79 public static function columnSort($a, $b) {
80 return strcasecmp(strlen($a) . $a, strlen($b) . $b);
81 }
82
83 /**
84 * Compare two column addresses
85 * Intended for use as a Callback function for reverse sorting column addresses by column
86 *
87 * @param string $a First column to test (e.g. 'AA')
88 * @param string $b Second column to test (e.g. 'Z')
89 * @return integer
90 */
91 public static function columnReverseSort($a, $b) {
92 return 1 - strcasecmp(strlen($a) . $a, strlen($b) . $b);
93 }
94
95 /**
96 * Compare two cell addresses
97 * Intended for use as a Callback function for sorting cell addresses by column and row
98 *
99 * @param string $a First cell to test (e.g. 'AA1')
100 * @param string $b Second cell to test (e.g. 'Z1')
101 * @return integer
102 */
103 public static function cellSort($a, $b) {
104 sscanf($a,'%[A-Z]%d', $ac, $ar);
105 sscanf($b,'%[A-Z]%d', $bc, $br);
106
107 if ($ar == $br) {
108 return strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
109 }
110 return ($ar < $br) ? -1 : 1;
111 }
112
113 /**
114 * Compare two cell addresses
115 * Intended for use as a Callback function for sorting cell addresses by column and row
116 *
117 * @param string $a First cell to test (e.g. 'AA1')
118 * @param string $b Second cell to test (e.g. 'Z1')
119 * @return integer
120 */
121 public static function cellReverseSort($a, $b) {
122 sscanf($a,'%[A-Z]%d', $ac, $ar);
123 sscanf($b,'%[A-Z]%d', $bc, $br);
124
125 if ($ar == $br) {
126 return 1 - strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
127 }
128 return ($ar < $br) ? 1 : -1;
129 }
130
131 /**
132 * Test whether a cell address falls within a defined range of cells
133 *
134 * @param string $cellAddress Address of the cell we're testing
135 * @param integer $beforeRow Number of the row we're inserting/deleting before
136 * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
137 * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
138 * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
139 * @return boolean
140 */
141 private static function cellAddressInDeleteRange($cellAddress, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols) {
142 list($cellColumn, $cellRow) = PHPExcel_Cell::coordinateFromString($cellAddress);
143 $cellColumnIndex = PHPExcel_Cell::columnIndexFromString($cellColumn);
144 // Is cell within the range of rows/columns if we're deleting
145 if ($pNumRows < 0 &&
146 ($cellRow >= ($beforeRow + $pNumRows)) &&
147 ($cellRow < $beforeRow)) {
148 return TRUE;
149 } elseif ($pNumCols < 0 &&
150 ($cellColumnIndex >= ($beforeColumnIndex + $pNumCols)) &&
151 ($cellColumnIndex < $beforeColumnIndex)) {
152 return TRUE;
153 }
154 return FALSE;
155 }
156
157 /**
158 * Update page breaks when inserting/deleting rows/columns
159 *
160 * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
161 * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
162 * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
163 * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
164 * @param integer $beforeRow Number of the row we're inserting/deleting before
165 * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
166 */
167 protected function _adjustPageBreaks(PHPExcel_Worksheet $pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
168 {
169 $aBreaks = $pSheet->getBreaks();
170 ($pNumCols > 0 || $pNumRows > 0) ?
171 uksort($aBreaks, array('PHPExcel_ReferenceHelper','cellReverseSort')) :
172 uksort($aBreaks, array('PHPExcel_ReferenceHelper','cellSort'));
173
174 foreach ($aBreaks as $key => $value) {
175 if (self::cellAddressInDeleteRange($key, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols)) {
176 // If we're deleting, then clear any defined breaks that are within the range
177 // of rows/columns that we're deleting
178 $pSheet->setBreak($key, PHPExcel_Worksheet::BREAK_NONE);
179 } else {
180 // Otherwise update any affected breaks by inserting a new break at the appropriate point
181 // and removing the old affected break
182 $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
183 if ($key != $newReference) {
184 $pSheet->setBreak($newReference, $value)
185 ->setBreak($key, PHPExcel_Worksheet::BREAK_NONE);
186 }
187 }
188 }
189 }
190
191 /**
192 * Update cell comments when inserting/deleting rows/columns
193 *
194 * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
195 * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
196 * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
197 * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
198 * @param integer $beforeRow Number of the row we're inserting/deleting before
199 * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
200 */
201 protected function _adjustComments($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
202 {
203 $aComments = $pSheet->getComments();
204 $aNewComments = array(); // the new array of all comments
205
206 foreach ($aComments as $key => &$value) {
207 // Any comments inside a deleted range will be ignored
208 if (!self::cellAddressInDeleteRange($key, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols)) {
209 // Otherwise build a new array of comments indexed by the adjusted cell reference
210 $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
211 $aNewComments[$newReference] = $value;
212 }
213 }
214 // Replace the comments array with the new set of comments
215 $pSheet->setComments($aNewComments);
216 }
217
218 /**
219 * Update hyperlinks when inserting/deleting rows/columns
220 *
221 * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
222 * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
223 * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
224 * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
225 * @param integer $beforeRow Number of the row we're inserting/deleting before
226 * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
227 */
228 protected function _adjustHyperlinks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
229 {
230 $aHyperlinkCollection = $pSheet->getHyperlinkCollection();
231 ($pNumCols > 0 || $pNumRows > 0) ?
232 uksort($aHyperlinkCollection, array('PHPExcel_ReferenceHelper','cellReverseSort')) :
233 uksort($aHyperlinkCollection, array('PHPExcel_ReferenceHelper','cellSort'));
234
235 foreach ($aHyperlinkCollection as $key => $value) {
236 $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
237 if ($key != $newReference) {
238 $pSheet->setHyperlink( $newReference, $value );
239 $pSheet->setHyperlink( $key, null );
240 }
241 }
242 }
243
244 /**
245 * Update data validations when inserting/deleting rows/columns
246 *
247 * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
248 * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
249 * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
250 * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
251 * @param integer $beforeRow Number of the row we're inserting/deleting before
252 * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
253 */
254 protected function _adjustDataValidations($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
255 {
256 $aDataValidationCollection = $pSheet->getDataValidationCollection();
257 ($pNumCols > 0 || $pNumRows > 0) ?
258 uksort($aDataValidationCollection, array('PHPExcel_ReferenceHelper','cellReverseSort')) :
259 uksort($aDataValidationCollection, array('PHPExcel_ReferenceHelper','cellSort'));
260 foreach ($aDataValidationCollection as $key => $value) {
261 $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
262 if ($key != $newReference) {
263 $pSheet->setDataValidation( $newReference, $value );
264 $pSheet->setDataValidation( $key, null );
265 }
266 }
267 }
268
269 /**
270 * Update merged cells when inserting/deleting rows/columns
271 *
272 * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
273 * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
274 * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
275 * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
276 * @param integer $beforeRow Number of the row we're inserting/deleting before
277 * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
278 */
279 protected function _adjustMergeCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
280 {
281 $aMergeCells = $pSheet->getMergeCells();
282 $aNewMergeCells = array(); // the new array of all merge cells
283 foreach ($aMergeCells as $key => &$value) {
284 $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
285 $aNewMergeCells[$newReference] = $newReference;
286 }
287 $pSheet->setMergeCells($aNewMergeCells); // replace the merge cells array
288 }
289
290 /**
291 * Update protected cells when inserting/deleting rows/columns
292 *
293 * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
294 * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
295 * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
296 * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
297 * @param integer $beforeRow Number of the row we're inserting/deleting before
298 * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
299 */
300 protected function _adjustProtectedCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
301 {
302 $aProtectedCells = $pSheet->getProtectedCells();
303 ($pNumCols > 0 || $pNumRows > 0) ?
304 uksort($aProtectedCells, array('PHPExcel_ReferenceHelper','cellReverseSort')) :
305 uksort($aProtectedCells, array('PHPExcel_ReferenceHelper','cellSort'));
306 foreach ($aProtectedCells as $key => $value) {
307 $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
308 if ($key != $newReference) {
309 $pSheet->protectCells( $newReference, $value, true );
310 $pSheet->unprotectCells( $key );
311 }
312 }
313 }
314
315 /**
316 * Update column dimensions when inserting/deleting rows/columns
317 *
318 * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
319 * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
320 * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
321 * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
322 * @param integer $beforeRow Number of the row we're inserting/deleting before
323 * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
324 */
325 protected function _adjustColumnDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
326 {
327 $aColumnDimensions = array_reverse($pSheet->getColumnDimensions(), true);
328 if (!empty($aColumnDimensions)) {
329 foreach ($aColumnDimensions as $objColumnDimension) {
330 $newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1', $pBefore, $pNumCols, $pNumRows);
331 list($newReference) = PHPExcel_Cell::coordinateFromString($newReference);
332 if ($objColumnDimension->getColumnIndex() != $newReference) {
333 $objColumnDimension->setColumnIndex($newReference);
334 }
335 }
336 $pSheet->refreshColumnDimensions();
337 }
338 }
339
340 /**
341 * Update row dimensions when inserting/deleting rows/columns
342 *
343 * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
344 * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
345 * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
346 * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
347 * @param integer $beforeRow Number of the row we're inserting/deleting before
348 * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
349 */
350 protected function _adjustRowDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
351 {
352 $aRowDimensions = array_reverse($pSheet->getRowDimensions(), true);
353 if (!empty($aRowDimensions)) {
354 foreach ($aRowDimensions as $objRowDimension) {
355 $newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex(), $pBefore, $pNumCols, $pNumRows);
356 list(, $newReference) = PHPExcel_Cell::coordinateFromString($newReference);
357 if ($objRowDimension->getRowIndex() != $newReference) {
358 $objRowDimension->setRowIndex($newReference);
359 }
360 }
361 $pSheet->refreshRowDimensions();
362
363 $copyDimension = $pSheet->getRowDimension($beforeRow - 1);
364 for ($i = $beforeRow; $i <= $beforeRow - 1 + $pNumRows; ++$i) {
365 $newDimension = $pSheet->getRowDimension($i);
366 $newDimension->setRowHeight($copyDimension->getRowHeight());
367 $newDimension->setVisible($copyDimension->getVisible());
368 $newDimension->setOutlineLevel($copyDimension->getOutlineLevel());
369 $newDimension->setCollapsed($copyDimension->getCollapsed());
370 }
371 }
372 }
373
374 /**
375 * Insert a new column or row, updating all possible related data
376 *
377 * @param string $pBefore Insert before this cell address (e.g. 'A1')
378 * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
379 * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
380 * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
381 * @throws PHPExcel_Exception
382 */
383 public function insertNewBefore($pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, PHPExcel_Worksheet $pSheet = NULL)
384 {
385 $remove = ($pNumCols < 0 || $pNumRows < 0);
386 $aCellCollection = $pSheet->getCellCollection();
387
388 // Get coordinates of $pBefore
389 $beforeColumn = 'A';
390 $beforeRow = 1;
391 list($beforeColumn, $beforeRow) = PHPExcel_Cell::coordinateFromString($pBefore);
392 $beforeColumnIndex = PHPExcel_Cell::columnIndexFromString($beforeColumn);
393
394 // Clear cells if we are removing columns or rows
395 $highestColumn = $pSheet->getHighestColumn();
396 $highestRow = $pSheet->getHighestRow();
397
398 // 1. Clear column strips if we are removing columns
399 if ($pNumCols < 0 && $beforeColumnIndex - 2 + $pNumCols > 0) {
400 for ($i = 1; $i <= $highestRow - 1; ++$i) {
401 for ($j = $beforeColumnIndex - 1 + $pNumCols; $j <= $beforeColumnIndex - 2; ++$j) {
402 $coordinate = PHPExcel_Cell::stringFromColumnIndex($j) . $i;
403 $pSheet->removeConditionalStyles($coordinate);
404 if ($pSheet->cellExists($coordinate)) {
405 $pSheet->getCell($coordinate)->setValueExplicit('', PHPExcel_Cell_DataType::TYPE_NULL);
406 $pSheet->getCell($coordinate)->setXfIndex(0);
407 }
408 }
409 }
410 }
411
412 // 2. Clear row strips if we are removing rows
413 if ($pNumRows < 0 && $beforeRow - 1 + $pNumRows > 0) {
414 for ($i = $beforeColumnIndex - 1; $i <= PHPExcel_Cell::columnIndexFromString($highestColumn) - 1; ++$i) {
415 for ($j = $beforeRow + $pNumRows; $j <= $beforeRow - 1; ++$j) {
416 $coordinate = PHPExcel_Cell::stringFromColumnIndex($i) . $j;
417 $pSheet->removeConditionalStyles($coordinate);
418 if ($pSheet->cellExists($coordinate)) {
419 $pSheet->getCell($coordinate)->setValueExplicit('', PHPExcel_Cell_DataType::TYPE_NULL);
420 $pSheet->getCell($coordinate)->setXfIndex(0);
421 }
422 }
423 }
424 }
425
426 // Loop through cells, bottom-up, and change cell coordinates
427 if($remove) {
428 // It's faster to reverse and pop than to use unshift, especially with large cell collections
429 $aCellCollection = array_reverse($aCellCollection);
430 }
431 while ($cellID = array_pop($aCellCollection)) {
432 $cell = $pSheet->getCell($cellID);
433 $cellIndex = PHPExcel_Cell::columnIndexFromString($cell->getColumn());
434
435 if ($cellIndex-1 + $pNumCols < 0) {
436 continue;
437 }
438
439 // New coordinates
440 $newCoordinates = PHPExcel_Cell::stringFromColumnIndex($cellIndex-1 + $pNumCols) . ($cell->getRow() + $pNumRows);
441
442 // Should the cell be updated? Move value and cellXf index from one cell to another.
443 if (($cellIndex >= $beforeColumnIndex) &&
444 ($cell->getRow() >= $beforeRow)) {
445
446 // Update cell styles
447 $pSheet->getCell($newCoordinates)->setXfIndex($cell->getXfIndex());
448
449 // Insert this cell at its new location
450 if ($cell->getDataType() == PHPExcel_Cell_DataType::TYPE_FORMULA) {
451 // Formula should be adjusted
452 $pSheet->getCell($newCoordinates)
453 ->setValue($this->updateFormulaReferences($cell->getValue(),
454 $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle()));
455 } else {
456 // Formula should not be adjusted
457 $pSheet->getCell($newCoordinates)->setValue($cell->getValue());
458 }
459
460 // Clear the original cell
461 $pSheet->getCellCacheController()->deleteCacheData($cellID);
462
463 } else {
464 /* We don't need to update styles for rows/columns before our insertion position,
465 but we do still need to adjust any formulae in those cells */
466 if ($cell->getDataType() == PHPExcel_Cell_DataType::TYPE_FORMULA) {
467 // Formula should be adjusted
468 $cell->setValue($this->updateFormulaReferences($cell->getValue(),
469 $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle()));
470 }
471
472 }
473 }
474
475 // Duplicate styles for the newly inserted cells
476 $highestColumn = $pSheet->getHighestColumn();
477 $highestRow = $pSheet->getHighestRow();
478
479 if ($pNumCols > 0 && $beforeColumnIndex - 2 > 0) {
480 for ($i = $beforeRow; $i <= $highestRow - 1; ++$i) {
481
482 // Style
483 $coordinate = PHPExcel_Cell::stringFromColumnIndex( $beforeColumnIndex - 2 ) . $i;
484 if ($pSheet->cellExists($coordinate)) {
485 $xfIndex = $pSheet->getCell($coordinate)->getXfIndex();
486 $conditionalStyles = $pSheet->conditionalStylesExists($coordinate) ?
487 $pSheet->getConditionalStyles($coordinate) : false;
488 for ($j = $beforeColumnIndex - 1; $j <= $beforeColumnIndex - 2 + $pNumCols; ++$j) {
489 $pSheet->getCellByColumnAndRow($j, $i)->setXfIndex($xfIndex);
490 if ($conditionalStyles) {
491 $cloned = array();
492 foreach ($conditionalStyles as $conditionalStyle) {
493 $cloned[] = clone $conditionalStyle;
494 }
495 $pSheet->setConditionalStyles(PHPExcel_Cell::stringFromColumnIndex($j) . $i, $cloned);
496 }
497 }
498 }
499
500 }
501 }
502
503 if ($pNumRows > 0 && $beforeRow - 1 > 0) {
504 for ($i = $beforeColumnIndex - 1; $i <= PHPExcel_Cell::columnIndexFromString($highestColumn) - 1; ++$i) {
505
506 // Style
507 $coordinate = PHPExcel_Cell::stringFromColumnIndex($i) . ($beforeRow - 1);
508 if ($pSheet->cellExists($coordinate)) {
509 $xfIndex = $pSheet->getCell($coordinate)->getXfIndex();
510 $conditionalStyles = $pSheet->conditionalStylesExists($coordinate) ?
511 $pSheet->getConditionalStyles($coordinate) : false;
512 for ($j = $beforeRow; $j <= $beforeRow - 1 + $pNumRows; ++$j) {
513 $pSheet->getCell(PHPExcel_Cell::stringFromColumnIndex($i) . $j)->setXfIndex($xfIndex);
514 if ($conditionalStyles) {
515 $cloned = array();
516 foreach ($conditionalStyles as $conditionalStyle) {
517 $cloned[] = clone $conditionalStyle;
518 }
519 $pSheet->setConditionalStyles(PHPExcel_Cell::stringFromColumnIndex($i) . $j, $cloned);
520 }
521 }
522 }
523 }
524 }
525
526 // Update worksheet: column dimensions
527 $this->_adjustColumnDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
528
529 // Update worksheet: row dimensions
530 $this->_adjustRowDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
531
532 // Update worksheet: page breaks
533 $this->_adjustPageBreaks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
534
535 // Update worksheet: comments
536 $this->_adjustComments($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
537
538 // Update worksheet: hyperlinks
539 $this->_adjustHyperlinks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
540
541 // Update worksheet: data validations
542 $this->_adjustDataValidations($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
543
544 // Update worksheet: merge cells
545 $this->_adjustMergeCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
546
547 // Update worksheet: protected cells
548 $this->_adjustProtectedCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
549
550 // Update worksheet: autofilter
551 $autoFilter = $pSheet->getAutoFilter();
552 $autoFilterRange = $autoFilter->getRange();
553 if (!empty($autoFilterRange)) {
554 if ($pNumCols != 0) {
555 $autoFilterColumns = array_keys($autoFilter->getColumns());
556 if (count($autoFilterColumns) > 0) {
557 sscanf($pBefore,'%[A-Z]%d', $column, $row);
558 $columnIndex = PHPExcel_Cell::columnIndexFromString($column);
559 list($rangeStart,$rangeEnd) = PHPExcel_Cell::rangeBoundaries($autoFilterRange);
560 if ($columnIndex <= $rangeEnd[0]) {
561 if ($pNumCols < 0) {
562 // If we're actually deleting any columns that fall within the autofilter range,
563 // then we delete any rules for those columns
564 $deleteColumn = $columnIndex + $pNumCols - 1;
565 $deleteCount = abs($pNumCols);
566 for ($i = 1; $i <= $deleteCount; ++$i) {
567 if (in_array(PHPExcel_Cell::stringFromColumnIndex($deleteColumn),$autoFilterColumns)) {
568 $autoFilter->clearColumn(PHPExcel_Cell::stringFromColumnIndex($deleteColumn));
569 }
570 ++$deleteColumn;
571 }
572 }
573 $startCol = ($columnIndex > $rangeStart[0]) ? $columnIndex : $rangeStart[0];
574
575 // Shuffle columns in autofilter range
576 if ($pNumCols > 0) {
577 // For insert, we shuffle from end to beginning to avoid overwriting
578 $startColID = PHPExcel_Cell::stringFromColumnIndex($startCol-1);
579 $toColID = PHPExcel_Cell::stringFromColumnIndex($startCol+$pNumCols-1);
580 $endColID = PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0]);
581
582 $startColRef = $startCol;
583 $endColRef = $rangeEnd[0];
584 $toColRef = $rangeEnd[0]+$pNumCols;
585
586 do {
587 $autoFilter->shiftColumn(PHPExcel_Cell::stringFromColumnIndex($endColRef-1),PHPExcel_Cell::stringFromColumnIndex($toColRef-1));
588 --$endColRef;
589 --$toColRef;
590 } while ($startColRef <= $endColRef);
591 } else {
592 // For delete, we shuffle from beginning to end to avoid overwriting
593 $startColID = PHPExcel_Cell::stringFromColumnIndex($startCol-1);
594 $toColID = PHPExcel_Cell::stringFromColumnIndex($startCol+$pNumCols-1);
595 $endColID = PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0]);
596 do {
597 $autoFilter->shiftColumn($startColID,$toColID);
598 ++$startColID;
599 ++$toColID;
600 } while ($startColID != $endColID);
601 }
602 }
603 }
604 }
605 $pSheet->setAutoFilter( $this->updateCellReference($autoFilterRange, $pBefore, $pNumCols, $pNumRows) );
606 }
607
608 // Update worksheet: freeze pane
609 if ($pSheet->getFreezePane() != '') {
610 $pSheet->freezePane( $this->updateCellReference($pSheet->getFreezePane(), $pBefore, $pNumCols, $pNumRows) );
611 }
612
613 // Page setup
614 if ($pSheet->getPageSetup()->isPrintAreaSet()) {
615 $pSheet->getPageSetup()->setPrintArea( $this->updateCellReference($pSheet->getPageSetup()->getPrintArea(), $pBefore, $pNumCols, $pNumRows) );
616 }
617
618 // Update worksheet: drawings
619 $aDrawings = $pSheet->getDrawingCollection();
620 foreach ($aDrawings as $objDrawing) {
621 $newReference = $this->updateCellReference($objDrawing->getCoordinates(), $pBefore, $pNumCols, $pNumRows);
622 if ($objDrawing->getCoordinates() != $newReference) {
623 $objDrawing->setCoordinates($newReference);
624 }
625 }
626
627 // Update workbook: named ranges
628 if (count($pSheet->getParent()->getNamedRanges()) > 0) {
629 foreach ($pSheet->getParent()->getNamedRanges() as $namedRange) {
630 if ($namedRange->getWorksheet()->getHashCode() == $pSheet->getHashCode()) {
631 $namedRange->setRange(
632 $this->updateCellReference($namedRange->getRange(), $pBefore, $pNumCols, $pNumRows)
633 );
634 }
635 }
636 }
637
638 // Garbage collect
639 $pSheet->garbageCollect();
640 }
641
642 /**
643 * Update references within formulas
644 *
645 * @param string $pFormula Formula to update
646 * @param int $pBefore Insert before this one
647 * @param int $pNumCols Number of columns to insert
648 * @param int $pNumRows Number of rows to insert
649 * @param string $sheetName Worksheet name/title
650 * @return string Updated formula
651 * @throws PHPExcel_Exception
652 */
653 public function updateFormulaReferences($pFormula = '', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, $sheetName = '') {
654 // Update cell references in the formula
655 $formulaBlocks = explode('"',$pFormula);
656 $i = false;
657 foreach($formulaBlocks as &$formulaBlock) {
658 // Ignore blocks that were enclosed in quotes (alternating entries in the $formulaBlocks array after the explode)
659 if ($i = !$i) {
660 $adjustCount = 0;
661 $newCellTokens = $cellTokens = array();
662 // Search for row ranges (e.g. 'Sheet1'!3:5 or 3:5) with or without $ absolutes (e.g. $3:5)
663 $matchCount = preg_match_all('/'.self::REFHELPER_REGEXP_ROWRANGE.'/i', ' '.$formulaBlock.' ', $matches, PREG_SET_ORDER);
664 if ($matchCount > 0) {
665 foreach($matches as $match) {
666 $fromString = ($match[2] > '') ? $match[2].'!' : '';
667 $fromString .= $match[3].':'.$match[4];
668 $modified3 = substr($this->updateCellReference('$A'.$match[3],$pBefore,$pNumCols,$pNumRows),2);
669 $modified4 = substr($this->updateCellReference('$A'.$match[4],$pBefore,$pNumCols,$pNumRows),2);
670
671 if ($match[3].':'.$match[4] !== $modified3.':'.$modified4) {
672 if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
673 $toString = ($match[2] > '') ? $match[2].'!' : '';
674 $toString .= $modified3.':'.$modified4;
675 // Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
676 $column = 100000;
677 $row = 10000000+trim($match[3],'$');
678 $cellIndex = $column.$row;
679
680 $newCellTokens[$cellIndex] = preg_quote($toString);
681 $cellTokens[$cellIndex] = '/(?<!\d\$\!)'.preg_quote($fromString).'(?!\d)/i';
682 ++$adjustCount;
683 }
684 }
685 }
686 }
687 // Search for column ranges (e.g. 'Sheet1'!C:E or C:E) with or without $ absolutes (e.g. $C:E)
688 $matchCount = preg_match_all('/'.self::REFHELPER_REGEXP_COLRANGE.'/i', ' '.$formulaBlock.' ', $matches, PREG_SET_ORDER);
689 if ($matchCount > 0) {
690 foreach($matches as $match) {
691 $fromString = ($match[2] > '') ? $match[2].'!' : '';
692 $fromString .= $match[3].':'.$match[4];
693 $modified3 = substr($this->updateCellReference($match[3].'$1',$pBefore,$pNumCols,$pNumRows),0,-2);
694 $modified4 = substr($this->updateCellReference($match[4].'$1',$pBefore,$pNumCols,$pNumRows),0,-2);
695
696 if ($match[3].':'.$match[4] !== $modified3.':'.$modified4) {
697 if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
698 $toString = ($match[2] > '') ? $match[2].'!' : '';
699 $toString .= $modified3.':'.$modified4;
700 // Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
701 $column = PHPExcel_Cell::columnIndexFromString(trim($match[3],'$')) + 100000;
702 $row = 10000000;
703 $cellIndex = $column.$row;
704
705 $newCellTokens[$cellIndex] = preg_quote($toString);
706 $cellTokens[$cellIndex] = '/(?<![A-Z\$\!])'.preg_quote($fromString).'(?![A-Z])/i';
707 ++$adjustCount;
708 }
709 }
710 }
711 }
712 // Search for cell ranges (e.g. 'Sheet1'!A3:C5 or A3:C5) with or without $ absolutes (e.g. $A1:C$5)
713 $matchCount = preg_match_all('/'.self::REFHELPER_REGEXP_CELLRANGE.'/i', ' '.$formulaBlock.' ', $matches, PREG_SET_ORDER);
714 if ($matchCount > 0) {
715 foreach($matches as $match) {
716 $fromString = ($match[2] > '') ? $match[2].'!' : '';
717 $fromString .= $match[3].':'.$match[4];
718 $modified3 = $this->updateCellReference($match[3],$pBefore,$pNumCols,$pNumRows);
719 $modified4 = $this->updateCellReference($match[4],$pBefore,$pNumCols,$pNumRows);
720
721 if ($match[3].$match[4] !== $modified3.$modified4) {
722 if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
723 $toString = ($match[2] > '') ? $match[2].'!' : '';
724 $toString .= $modified3.':'.$modified4;
725 list($column,$row) = PHPExcel_Cell::coordinateFromString($match[3]);
726 // Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
727 $column = PHPExcel_Cell::columnIndexFromString(trim($column,'$')) + 100000;
728 $row = trim($row,'$') + 10000000;
729 $cellIndex = $column.$row;
730
731 $newCellTokens[$cellIndex] = preg_quote($toString);
732 $cellTokens[$cellIndex] = '/(?<![A-Z]\$\!)'.preg_quote($fromString).'(?!\d)/i';
733 ++$adjustCount;
734 }
735 }
736 }
737 }
738 // Search for cell references (e.g. 'Sheet1'!A3 or C5) with or without $ absolutes (e.g. $A1 or C$5)
739 $matchCount = preg_match_all('/'.self::REFHELPER_REGEXP_CELLREF.'/i', ' '.$formulaBlock.' ', $matches, PREG_SET_ORDER);
740
741 if ($matchCount > 0) {
742 foreach($matches as $match) {
743 $fromString = ($match[2] > '') ? $match[2].'!' : '';
744 $fromString .= $match[3];
745
746 $modified3 = $this->updateCellReference($match[3],$pBefore,$pNumCols,$pNumRows);
747 if ($match[3] !== $modified3) {
748 if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
749 $toString = ($match[2] > '') ? $match[2].'!' : '';
750 $toString .= $modified3;
751 list($column,$row) = PHPExcel_Cell::coordinateFromString($match[3]);
752 // Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
753 $column = PHPExcel_Cell::columnIndexFromString(trim($column,'$')) + 100000;
754 $row = trim($row,'$') + 10000000;
755 $cellIndex = $row . $column;
756
757 $newCellTokens[$cellIndex] = preg_quote($toString);
758 $cellTokens[$cellIndex] = '/(?<![A-Z\$\!])'.preg_quote($fromString).'(?!\d)/i';
759 ++$adjustCount;
760 }
761 }
762 }
763 }
764 if ($adjustCount > 0) {
765 if ($pNumCols > 0 || $pNumRows > 0) {
766 krsort($cellTokens);
767 krsort($newCellTokens);
768 } else {
769 ksort($cellTokens);
770 ksort($newCellTokens);
771 } // Update cell references in the formula
772 $formulaBlock = str_replace('\\','',preg_replace($cellTokens,$newCellTokens,$formulaBlock));
773 }
774 }
775 }
776 unset($formulaBlock);
777
778 // Then rebuild the formula string
779 return implode('"',$formulaBlocks);
780 }
781
782 /**
783 * Update cell reference
784 *
785 * @param string $pCellRange Cell range
786 * @param int $pBefore Insert before this one
787 * @param int $pNumCols Number of columns to increment
788 * @param int $pNumRows Number of rows to increment
789 * @return string Updated cell range
790 * @throws PHPExcel_Exception
791 */
792 public function updateCellReference($pCellRange = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) {
793 // Is it in another worksheet? Will not have to update anything.
794 if (strpos($pCellRange, "!") !== false) {
795 return $pCellRange;
796 // Is it a range or a single cell?
797 } elseif (strpos($pCellRange, ':') === false && strpos($pCellRange, ',') === false) {
798 // Single cell
799 return $this->_updateSingleCellReference($pCellRange, $pBefore, $pNumCols, $pNumRows);
800 } elseif (strpos($pCellRange, ':') !== false || strpos($pCellRange, ',') !== false) {
801 // Range
802 return $this->_updateCellRange($pCellRange, $pBefore, $pNumCols, $pNumRows);
803 } else {
804 // Return original
805 return $pCellRange;
806 }
807 }
808
809 /**
810 * Update named formulas (i.e. containing worksheet references / named ranges)
811 *
812 * @param PHPExcel $pPhpExcel Object to update
813 * @param string $oldName Old name (name to replace)
814 * @param string $newName New name
815 */
816 public function updateNamedFormulas(PHPExcel $pPhpExcel, $oldName = '', $newName = '') {
817 if ($oldName == '') {
818 return;
819 }
820
821 foreach ($pPhpExcel->getWorksheetIterator() as $sheet) {
822 foreach ($sheet->getCellCollection(false) as $cellID) {
823 $cell = $sheet->getCell($cellID);
824 if (($cell !== NULL) && ($cell->getDataType() == PHPExcel_Cell_DataType::TYPE_FORMULA)) {
825 $formula = $cell->getValue();
826 if (strpos($formula, $oldName) !== false) {
827 $formula = str_replace("'" . $oldName . "'!", "'" . $newName . "'!", $formula);
828 $formula = str_replace($oldName . "!", $newName . "!", $formula);
829 $cell->setValueExplicit($formula, PHPExcel_Cell_DataType::TYPE_FORMULA);
830 }
831 }
832 }
833 }
834 }
835
836 /**
837 * Update cell range
838 *
839 * @param string $pCellRange Cell range (e.g. 'B2:D4', 'B:C' or '2:3')
840 * @param int $pBefore Insert before this one
841 * @param int $pNumCols Number of columns to increment
842 * @param int $pNumRows Number of rows to increment
843 * @return string Updated cell range
844 * @throws PHPExcel_Exception
845 */
846 private function _updateCellRange($pCellRange = 'A1:A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) {
847 if (strpos($pCellRange,':') !== false || strpos($pCellRange, ',') !== false) {
848 // Update range
849 $range = PHPExcel_Cell::splitRange($pCellRange);
850 $ic = count($range);
851 for ($i = 0; $i < $ic; ++$i) {
852 $jc = count($range[$i]);
853 for ($j = 0; $j < $jc; ++$j) {
854 if (ctype_alpha($range[$i][$j])) {
855 $r = PHPExcel_Cell::coordinateFromString($this->_updateSingleCellReference($range[$i][$j].'1', $pBefore, $pNumCols, $pNumRows));
856 $range[$i][$j] = $r[0];
857 } elseif(ctype_digit($range[$i][$j])) {
858 $r = PHPExcel_Cell::coordinateFromString($this->_updateSingleCellReference('A'.$range[$i][$j], $pBefore, $pNumCols, $pNumRows));
859 $range[$i][$j] = $r[1];
860 } else {
861 $range[$i][$j] = $this->_updateSingleCellReference($range[$i][$j], $pBefore, $pNumCols, $pNumRows);
862 }
863 }
864 }
865
866 // Recreate range string
867 return PHPExcel_Cell::buildRange($range);
868 } else {
869 throw new PHPExcel_Exception("Only cell ranges may be passed to this method.");
870 }
871 }
872
873 /**
874 * Update single cell reference
875 *
876 * @param string $pCellReference Single cell reference
877 * @param int $pBefore Insert before this one
878 * @param int $pNumCols Number of columns to increment
879 * @param int $pNumRows Number of rows to increment
880 * @return string Updated cell reference
881 * @throws PHPExcel_Exception
882 */
883 private function _updateSingleCellReference($pCellReference = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) {
884 if (strpos($pCellReference, ':') === false && strpos($pCellReference, ',') === false) {
885 // Get coordinates of $pBefore
886 list($beforeColumn, $beforeRow) = PHPExcel_Cell::coordinateFromString( $pBefore );
887
888 // Get coordinates of $pCellReference
889 list($newColumn, $newRow) = PHPExcel_Cell::coordinateFromString( $pCellReference );
890
891 // Verify which parts should be updated
892 $updateColumn = (($newColumn{0} != '$') && ($beforeColumn{0} != '$') &&
893 PHPExcel_Cell::columnIndexFromString($newColumn) >= PHPExcel_Cell::columnIndexFromString($beforeColumn));
894 $updateRow = (($newRow{0} != '$') && ($beforeRow{0} != '$') &&
895 $newRow >= $beforeRow);
896
897 // Create new column reference
898 if ($updateColumn) {
899 $newColumn = PHPExcel_Cell::stringFromColumnIndex( PHPExcel_Cell::columnIndexFromString($newColumn) - 1 + $pNumCols );
900 }
901
902 // Create new row reference
903 if ($updateRow) {
904 $newRow = $newRow + $pNumRows;
905 }
906
907 // Return new reference
908 return $newColumn . $newRow;
909 } else {
910 throw new PHPExcel_Exception("Only single cell references may be passed to this method.");
911 }
912 }
913
914 /**
915 * __clone implementation. Cloning should not be allowed in a Singleton!
916 *
917 * @throws PHPExcel_Exception
918 */
919 public final function __clone() {
920 throw new PHPExcel_Exception("Cloning a Singleton is not allowed!");
921 }
922 }
923