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 1.8.0, 2014-03-02 |
---|
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 | } |
---|