Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Worksheet;
   4  
   5  use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
   6  use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
   7  
   8  /**
   9   * <code>
  10   * Paper size taken from Office Open XML Part 4 - Markup Language Reference, page 1988:.
  11   *
  12   * 1 = Letter paper (8.5 in. by 11 in.)
  13   * 2 = Letter small paper (8.5 in. by 11 in.)
  14   * 3 = Tabloid paper (11 in. by 17 in.)
  15   * 4 = Ledger paper (17 in. by 11 in.)
  16   * 5 = Legal paper (8.5 in. by 14 in.)
  17   * 6 = Statement paper (5.5 in. by 8.5 in.)
  18   * 7 = Executive paper (7.25 in. by 10.5 in.)
  19   * 8 = A3 paper (297 mm by 420 mm)
  20   * 9 = A4 paper (210 mm by 297 mm)
  21   * 10 = A4 small paper (210 mm by 297 mm)
  22   * 11 = A5 paper (148 mm by 210 mm)
  23   * 12 = B4 paper (250 mm by 353 mm)
  24   * 13 = B5 paper (176 mm by 250 mm)
  25   * 14 = Folio paper (8.5 in. by 13 in.)
  26   * 15 = Quarto paper (215 mm by 275 mm)
  27   * 16 = Standard paper (10 in. by 14 in.)
  28   * 17 = Standard paper (11 in. by 17 in.)
  29   * 18 = Note paper (8.5 in. by 11 in.)
  30   * 19 = #9 envelope (3.875 in. by 8.875 in.)
  31   * 20 = #10 envelope (4.125 in. by 9.5 in.)
  32   * 21 = #11 envelope (4.5 in. by 10.375 in.)
  33   * 22 = #12 envelope (4.75 in. by 11 in.)
  34   * 23 = #14 envelope (5 in. by 11.5 in.)
  35   * 24 = C paper (17 in. by 22 in.)
  36   * 25 = D paper (22 in. by 34 in.)
  37   * 26 = E paper (34 in. by 44 in.)
  38   * 27 = DL envelope (110 mm by 220 mm)
  39   * 28 = C5 envelope (162 mm by 229 mm)
  40   * 29 = C3 envelope (324 mm by 458 mm)
  41   * 30 = C4 envelope (229 mm by 324 mm)
  42   * 31 = C6 envelope (114 mm by 162 mm)
  43   * 32 = C65 envelope (114 mm by 229 mm)
  44   * 33 = B4 envelope (250 mm by 353 mm)
  45   * 34 = B5 envelope (176 mm by 250 mm)
  46   * 35 = B6 envelope (176 mm by 125 mm)
  47   * 36 = Italy envelope (110 mm by 230 mm)
  48   * 37 = Monarch envelope (3.875 in. by 7.5 in.).
  49   * 38 = 6 3/4 envelope (3.625 in. by 6.5 in.)
  50   * 39 = US standard fanfold (14.875 in. by 11 in.)
  51   * 40 = German standard fanfold (8.5 in. by 12 in.)
  52   * 41 = German legal fanfold (8.5 in. by 13 in.)
  53   * 42 = ISO B4 (250 mm by 353 mm)
  54   * 43 = Japanese double postcard (200 mm by 148 mm)
  55   * 44 = Standard paper (9 in. by 11 in.)
  56   * 45 = Standard paper (10 in. by 11 in.)
  57   * 46 = Standard paper (15 in. by 11 in.)
  58   * 47 = Invite envelope (220 mm by 220 mm)
  59   * 50 = Letter extra paper (9.275 in. by 12 in.)
  60   * 51 = Legal extra paper (9.275 in. by 15 in.)
  61   * 52 = Tabloid extra paper (11.69 in. by 18 in.)
  62   * 53 = A4 extra paper (236 mm by 322 mm)
  63   * 54 = Letter transverse paper (8.275 in. by 11 in.)
  64   * 55 = A4 transverse paper (210 mm by 297 mm)
  65   * 56 = Letter extra transverse paper (9.275 in. by 12 in.)
  66   * 57 = SuperA/SuperA/A4 paper (227 mm by 356 mm)
  67   * 58 = SuperB/SuperB/A3 paper (305 mm by 487 mm)
  68   * 59 = Letter plus paper (8.5 in. by 12.69 in.)
  69   * 60 = A4 plus paper (210 mm by 330 mm)
  70   * 61 = A5 transverse paper (148 mm by 210 mm)
  71   * 62 = JIS B5 transverse paper (182 mm by 257 mm)
  72   * 63 = A3 extra paper (322 mm by 445 mm)
  73   * 64 = A5 extra paper (174 mm by 235 mm)
  74   * 65 = ISO B5 extra paper (201 mm by 276 mm)
  75   * 66 = A2 paper (420 mm by 594 mm)
  76   * 67 = A3 transverse paper (297 mm by 420 mm)
  77   * 68 = A3 extra transverse paper (322 mm by 445 mm)
  78   * </code>
  79   */
  80  class PageSetup
  81  {
  82      // Paper size
  83      const PAPERSIZE_LETTER = 1;
  84      const PAPERSIZE_LETTER_SMALL = 2;
  85      const PAPERSIZE_TABLOID = 3;
  86      const PAPERSIZE_LEDGER = 4;
  87      const PAPERSIZE_LEGAL = 5;
  88      const PAPERSIZE_STATEMENT = 6;
  89      const PAPERSIZE_EXECUTIVE = 7;
  90      const PAPERSIZE_A3 = 8;
  91      const PAPERSIZE_A4 = 9;
  92      const PAPERSIZE_A4_SMALL = 10;
  93      const PAPERSIZE_A5 = 11;
  94      const PAPERSIZE_B4 = 12;
  95      const PAPERSIZE_B5 = 13;
  96      const PAPERSIZE_FOLIO = 14;
  97      const PAPERSIZE_QUARTO = 15;
  98      const PAPERSIZE_STANDARD_1 = 16;
  99      const PAPERSIZE_STANDARD_2 = 17;
 100      const PAPERSIZE_NOTE = 18;
 101      const PAPERSIZE_NO9_ENVELOPE = 19;
 102      const PAPERSIZE_NO10_ENVELOPE = 20;
 103      const PAPERSIZE_NO11_ENVELOPE = 21;
 104      const PAPERSIZE_NO12_ENVELOPE = 22;
 105      const PAPERSIZE_NO14_ENVELOPE = 23;
 106      const PAPERSIZE_C = 24;
 107      const PAPERSIZE_D = 25;
 108      const PAPERSIZE_E = 26;
 109      const PAPERSIZE_DL_ENVELOPE = 27;
 110      const PAPERSIZE_C5_ENVELOPE = 28;
 111      const PAPERSIZE_C3_ENVELOPE = 29;
 112      const PAPERSIZE_C4_ENVELOPE = 30;
 113      const PAPERSIZE_C6_ENVELOPE = 31;
 114      const PAPERSIZE_C65_ENVELOPE = 32;
 115      const PAPERSIZE_B4_ENVELOPE = 33;
 116      const PAPERSIZE_B5_ENVELOPE = 34;
 117      const PAPERSIZE_B6_ENVELOPE = 35;
 118      const PAPERSIZE_ITALY_ENVELOPE = 36;
 119      const PAPERSIZE_MONARCH_ENVELOPE = 37;
 120      const PAPERSIZE_6_3_4_ENVELOPE = 38;
 121      const PAPERSIZE_US_STANDARD_FANFOLD = 39;
 122      const PAPERSIZE_GERMAN_STANDARD_FANFOLD = 40;
 123      const PAPERSIZE_GERMAN_LEGAL_FANFOLD = 41;
 124      const PAPERSIZE_ISO_B4 = 42;
 125      const PAPERSIZE_JAPANESE_DOUBLE_POSTCARD = 43;
 126      const PAPERSIZE_STANDARD_PAPER_1 = 44;
 127      const PAPERSIZE_STANDARD_PAPER_2 = 45;
 128      const PAPERSIZE_STANDARD_PAPER_3 = 46;
 129      const PAPERSIZE_INVITE_ENVELOPE = 47;
 130      const PAPERSIZE_LETTER_EXTRA_PAPER = 48;
 131      const PAPERSIZE_LEGAL_EXTRA_PAPER = 49;
 132      const PAPERSIZE_TABLOID_EXTRA_PAPER = 50;
 133      const PAPERSIZE_A4_EXTRA_PAPER = 51;
 134      const PAPERSIZE_LETTER_TRANSVERSE_PAPER = 52;
 135      const PAPERSIZE_A4_TRANSVERSE_PAPER = 53;
 136      const PAPERSIZE_LETTER_EXTRA_TRANSVERSE_PAPER = 54;
 137      const PAPERSIZE_SUPERA_SUPERA_A4_PAPER = 55;
 138      const PAPERSIZE_SUPERB_SUPERB_A3_PAPER = 56;
 139      const PAPERSIZE_LETTER_PLUS_PAPER = 57;
 140      const PAPERSIZE_A4_PLUS_PAPER = 58;
 141      const PAPERSIZE_A5_TRANSVERSE_PAPER = 59;
 142      const PAPERSIZE_JIS_B5_TRANSVERSE_PAPER = 60;
 143      const PAPERSIZE_A3_EXTRA_PAPER = 61;
 144      const PAPERSIZE_A5_EXTRA_PAPER = 62;
 145      const PAPERSIZE_ISO_B5_EXTRA_PAPER = 63;
 146      const PAPERSIZE_A2_PAPER = 64;
 147      const PAPERSIZE_A3_TRANSVERSE_PAPER = 65;
 148      const PAPERSIZE_A3_EXTRA_TRANSVERSE_PAPER = 66;
 149  
 150      // Page orientation
 151      const ORIENTATION_DEFAULT = 'default';
 152      const ORIENTATION_LANDSCAPE = 'landscape';
 153      const ORIENTATION_PORTRAIT = 'portrait';
 154  
 155      // Print Range Set Method
 156      const SETPRINTRANGE_OVERWRITE = 'O';
 157      const SETPRINTRANGE_INSERT = 'I';
 158  
 159      const PAGEORDER_OVER_THEN_DOWN = 'overThenDown';
 160      const PAGEORDER_DOWN_THEN_OVER = 'downThenOver';
 161  
 162      /**
 163       * Paper size.
 164       *
 165       * @var int
 166       */
 167      private $paperSize = self::PAPERSIZE_LETTER;
 168  
 169      /**
 170       * Orientation.
 171       *
 172       * @var string
 173       */
 174      private $orientation = self::ORIENTATION_DEFAULT;
 175  
 176      /**
 177       * Scale (Print Scale).
 178       *
 179       * Print scaling. Valid values range from 10 to 400
 180       * This setting is overridden when fitToWidth and/or fitToHeight are in use
 181       *
 182       * @var null|int
 183       */
 184      private $scale = 100;
 185  
 186      /**
 187       * Fit To Page
 188       * Whether scale or fitToWith / fitToHeight applies.
 189       *
 190       * @var bool
 191       */
 192      private $fitToPage = false;
 193  
 194      /**
 195       * Fit To Height
 196       * Number of vertical pages to fit on.
 197       *
 198       * @var null|int
 199       */
 200      private $fitToHeight = 1;
 201  
 202      /**
 203       * Fit To Width
 204       * Number of horizontal pages to fit on.
 205       *
 206       * @var null|int
 207       */
 208      private $fitToWidth = 1;
 209  
 210      /**
 211       * Columns to repeat at left.
 212       *
 213       * @var array Containing start column and end column, empty array if option unset
 214       */
 215      private $columnsToRepeatAtLeft = ['', ''];
 216  
 217      /**
 218       * Rows to repeat at top.
 219       *
 220       * @var array Containing start row number and end row number, empty array if option unset
 221       */
 222      private $rowsToRepeatAtTop = [0, 0];
 223  
 224      /**
 225       * Center page horizontally.
 226       *
 227       * @var bool
 228       */
 229      private $horizontalCentered = false;
 230  
 231      /**
 232       * Center page vertically.
 233       *
 234       * @var bool
 235       */
 236      private $verticalCentered = false;
 237  
 238      /**
 239       * Print area.
 240       *
 241       * @var string
 242       */
 243      private $printArea;
 244  
 245      /**
 246       * First page number.
 247       *
 248       * @var int
 249       */
 250      private $firstPageNumber;
 251  
 252      private $pageOrder = self::PAGEORDER_DOWN_THEN_OVER;
 253  
 254      /**
 255       * Create a new PageSetup.
 256       */
 257      public function __construct()
 258      {
 259      }
 260  
 261      /**
 262       * Get Paper Size.
 263       *
 264       * @return int
 265       */
 266      public function getPaperSize()
 267      {
 268          return $this->paperSize;
 269      }
 270  
 271      /**
 272       * Set Paper Size.
 273       *
 274       * @param int $pValue see self::PAPERSIZE_*
 275       *
 276       * @return $this
 277       */
 278      public function setPaperSize($pValue)
 279      {
 280          $this->paperSize = $pValue;
 281  
 282          return $this;
 283      }
 284  
 285      /**
 286       * Get Orientation.
 287       *
 288       * @return string
 289       */
 290      public function getOrientation()
 291      {
 292          return $this->orientation;
 293      }
 294  
 295      /**
 296       * Set Orientation.
 297       *
 298       * @param string $pValue see self::ORIENTATION_*
 299       *
 300       * @return $this
 301       */
 302      public function setOrientation($pValue)
 303      {
 304          $this->orientation = $pValue;
 305  
 306          return $this;
 307      }
 308  
 309      /**
 310       * Get Scale.
 311       *
 312       * @return null|int
 313       */
 314      public function getScale()
 315      {
 316          return $this->scale;
 317      }
 318  
 319      /**
 320       * Set Scale.
 321       * Print scaling. Valid values range from 10 to 400
 322       * This setting is overridden when fitToWidth and/or fitToHeight are in use.
 323       *
 324       * @param null|int $pValue
 325       * @param bool $pUpdate Update fitToPage so scaling applies rather than fitToHeight / fitToWidth
 326       *
 327       * @return $this
 328       */
 329      public function setScale($pValue, $pUpdate = true)
 330      {
 331          // Microsoft Office Excel 2007 only allows setting a scale between 10 and 400 via the user interface,
 332          // but it is apparently still able to handle any scale >= 0, where 0 results in 100
 333          if (($pValue >= 0) || $pValue === null) {
 334              $this->scale = $pValue;
 335              if ($pUpdate) {
 336                  $this->fitToPage = false;
 337              }
 338          } else {
 339              throw new PhpSpreadsheetException('Scale must not be negative');
 340          }
 341  
 342          return $this;
 343      }
 344  
 345      /**
 346       * Get Fit To Page.
 347       *
 348       * @return bool
 349       */
 350      public function getFitToPage()
 351      {
 352          return $this->fitToPage;
 353      }
 354  
 355      /**
 356       * Set Fit To Page.
 357       *
 358       * @param bool $pValue
 359       *
 360       * @return $this
 361       */
 362      public function setFitToPage($pValue)
 363      {
 364          $this->fitToPage = $pValue;
 365  
 366          return $this;
 367      }
 368  
 369      /**
 370       * Get Fit To Height.
 371       *
 372       * @return null|int
 373       */
 374      public function getFitToHeight()
 375      {
 376          return $this->fitToHeight;
 377      }
 378  
 379      /**
 380       * Set Fit To Height.
 381       *
 382       * @param null|int $pValue
 383       * @param bool $pUpdate Update fitToPage so it applies rather than scaling
 384       *
 385       * @return $this
 386       */
 387      public function setFitToHeight($pValue, $pUpdate = true)
 388      {
 389          $this->fitToHeight = $pValue;
 390          if ($pUpdate) {
 391              $this->fitToPage = true;
 392          }
 393  
 394          return $this;
 395      }
 396  
 397      /**
 398       * Get Fit To Width.
 399       *
 400       * @return null|int
 401       */
 402      public function getFitToWidth()
 403      {
 404          return $this->fitToWidth;
 405      }
 406  
 407      /**
 408       * Set Fit To Width.
 409       *
 410       * @param null|int $pValue
 411       * @param bool $pUpdate Update fitToPage so it applies rather than scaling
 412       *
 413       * @return $this
 414       */
 415      public function setFitToWidth($pValue, $pUpdate = true)
 416      {
 417          $this->fitToWidth = $pValue;
 418          if ($pUpdate) {
 419              $this->fitToPage = true;
 420          }
 421  
 422          return $this;
 423      }
 424  
 425      /**
 426       * Is Columns to repeat at left set?
 427       *
 428       * @return bool
 429       */
 430      public function isColumnsToRepeatAtLeftSet()
 431      {
 432          if (is_array($this->columnsToRepeatAtLeft)) {
 433              if ($this->columnsToRepeatAtLeft[0] != '' && $this->columnsToRepeatAtLeft[1] != '') {
 434                  return true;
 435              }
 436          }
 437  
 438          return false;
 439      }
 440  
 441      /**
 442       * Get Columns to repeat at left.
 443       *
 444       * @return array Containing start column and end column, empty array if option unset
 445       */
 446      public function getColumnsToRepeatAtLeft()
 447      {
 448          return $this->columnsToRepeatAtLeft;
 449      }
 450  
 451      /**
 452       * Set Columns to repeat at left.
 453       *
 454       * @param array $pValue Containing start column and end column, empty array if option unset
 455       *
 456       * @return $this
 457       */
 458      public function setColumnsToRepeatAtLeft(array $pValue)
 459      {
 460          $this->columnsToRepeatAtLeft = $pValue;
 461  
 462          return $this;
 463      }
 464  
 465      /**
 466       * Set Columns to repeat at left by start and end.
 467       *
 468       * @param string $pStart eg: 'A'
 469       * @param string $pEnd eg: 'B'
 470       *
 471       * @return $this
 472       */
 473      public function setColumnsToRepeatAtLeftByStartAndEnd($pStart, $pEnd)
 474      {
 475          $this->columnsToRepeatAtLeft = [$pStart, $pEnd];
 476  
 477          return $this;
 478      }
 479  
 480      /**
 481       * Is Rows to repeat at top set?
 482       *
 483       * @return bool
 484       */
 485      public function isRowsToRepeatAtTopSet()
 486      {
 487          if (is_array($this->rowsToRepeatAtTop)) {
 488              if ($this->rowsToRepeatAtTop[0] != 0 && $this->rowsToRepeatAtTop[1] != 0) {
 489                  return true;
 490              }
 491          }
 492  
 493          return false;
 494      }
 495  
 496      /**
 497       * Get Rows to repeat at top.
 498       *
 499       * @return array Containing start column and end column, empty array if option unset
 500       */
 501      public function getRowsToRepeatAtTop()
 502      {
 503          return $this->rowsToRepeatAtTop;
 504      }
 505  
 506      /**
 507       * Set Rows to repeat at top.
 508       *
 509       * @param array $pValue Containing start column and end column, empty array if option unset
 510       *
 511       * @return $this
 512       */
 513      public function setRowsToRepeatAtTop(array $pValue)
 514      {
 515          $this->rowsToRepeatAtTop = $pValue;
 516  
 517          return $this;
 518      }
 519  
 520      /**
 521       * Set Rows to repeat at top by start and end.
 522       *
 523       * @param int $pStart eg: 1
 524       * @param int $pEnd eg: 1
 525       *
 526       * @return $this
 527       */
 528      public function setRowsToRepeatAtTopByStartAndEnd($pStart, $pEnd)
 529      {
 530          $this->rowsToRepeatAtTop = [$pStart, $pEnd];
 531  
 532          return $this;
 533      }
 534  
 535      /**
 536       * Get center page horizontally.
 537       *
 538       * @return bool
 539       */
 540      public function getHorizontalCentered()
 541      {
 542          return $this->horizontalCentered;
 543      }
 544  
 545      /**
 546       * Set center page horizontally.
 547       *
 548       * @param bool $value
 549       *
 550       * @return $this
 551       */
 552      public function setHorizontalCentered($value)
 553      {
 554          $this->horizontalCentered = $value;
 555  
 556          return $this;
 557      }
 558  
 559      /**
 560       * Get center page vertically.
 561       *
 562       * @return bool
 563       */
 564      public function getVerticalCentered()
 565      {
 566          return $this->verticalCentered;
 567      }
 568  
 569      /**
 570       * Set center page vertically.
 571       *
 572       * @param bool $value
 573       *
 574       * @return $this
 575       */
 576      public function setVerticalCentered($value)
 577      {
 578          $this->verticalCentered = $value;
 579  
 580          return $this;
 581      }
 582  
 583      /**
 584       * Get print area.
 585       *
 586       * @param int $index Identifier for a specific print area range if several ranges have been set
 587       *                            Default behaviour, or a index value of 0, will return all ranges as a comma-separated string
 588       *                            Otherwise, the specific range identified by the value of $index will be returned
 589       *                            Print areas are numbered from 1
 590       *
 591       * @return string
 592       */
 593      public function getPrintArea($index = 0)
 594      {
 595          if ($index == 0) {
 596              return $this->printArea;
 597          }
 598          $printAreas = explode(',', $this->printArea);
 599          if (isset($printAreas[$index - 1])) {
 600              return $printAreas[$index - 1];
 601          }
 602  
 603          throw new PhpSpreadsheetException('Requested Print Area does not exist');
 604      }
 605  
 606      /**
 607       * Is print area set?
 608       *
 609       * @param int $index Identifier for a specific print area range if several ranges have been set
 610       *                            Default behaviour, or an index value of 0, will identify whether any print range is set
 611       *                            Otherwise, existence of the range identified by the value of $index will be returned
 612       *                            Print areas are numbered from 1
 613       *
 614       * @return bool
 615       */
 616      public function isPrintAreaSet($index = 0)
 617      {
 618          if ($index == 0) {
 619              return $this->printArea !== null;
 620          }
 621          $printAreas = explode(',', $this->printArea);
 622  
 623          return isset($printAreas[$index - 1]);
 624      }
 625  
 626      /**
 627       * Clear a print area.
 628       *
 629       * @param int $index Identifier for a specific print area range if several ranges have been set
 630       *                            Default behaviour, or an index value of 0, will clear all print ranges that are set
 631       *                            Otherwise, the range identified by the value of $index will be removed from the series
 632       *                            Print areas are numbered from 1
 633       *
 634       * @return $this
 635       */
 636      public function clearPrintArea($index = 0)
 637      {
 638          if ($index == 0) {
 639              $this->printArea = null;
 640          } else {
 641              $printAreas = explode(',', $this->printArea);
 642              if (isset($printAreas[$index - 1])) {
 643                  unset($printAreas[$index - 1]);
 644                  $this->printArea = implode(',', $printAreas);
 645              }
 646          }
 647  
 648          return $this;
 649      }
 650  
 651      /**
 652       * Set print area. e.g. 'A1:D10' or 'A1:D10,G5:M20'.
 653       *
 654       * @param string $value
 655       * @param int $index Identifier for a specific print area range allowing several ranges to be set
 656       *                            When the method is "O"verwrite, then a positive integer index will overwrite that indexed
 657       *                                entry in the print areas list; a negative index value will identify which entry to
 658       *                                overwrite working bacward through the print area to the list, with the last entry as -1.
 659       *                                Specifying an index value of 0, will overwrite <b>all</b> existing print ranges.
 660       *                            When the method is "I"nsert, then a positive index will insert after that indexed entry in
 661       *                                the print areas list, while a negative index will insert before the indexed entry.
 662       *                                Specifying an index value of 0, will always append the new print range at the end of the
 663       *                                list.
 664       *                            Print areas are numbered from 1
 665       * @param string $method Determines the method used when setting multiple print areas
 666       *                            Default behaviour, or the "O" method, overwrites existing print area
 667       *                            The "I" method, inserts the new print area before any specified index, or at the end of the list
 668       *
 669       * @return $this
 670       */
 671      public function setPrintArea($value, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
 672      {
 673          if (strpos($value, '!') !== false) {
 674              throw new PhpSpreadsheetException('Cell coordinate must not specify a worksheet.');
 675          } elseif (strpos($value, ':') === false) {
 676              throw new PhpSpreadsheetException('Cell coordinate must be a range of cells.');
 677          } elseif (strpos($value, '$') !== false) {
 678              throw new PhpSpreadsheetException('Cell coordinate must not be absolute.');
 679          }
 680          $value = strtoupper($value);
 681          if (!$this->printArea) {
 682              $index = 0;
 683          }
 684  
 685          if ($method == self::SETPRINTRANGE_OVERWRITE) {
 686              if ($index == 0) {
 687                  $this->printArea = $value;
 688              } else {
 689                  $printAreas = explode(',', $this->printArea);
 690                  if ($index < 0) {
 691                      $index = count($printAreas) - abs($index) + 1;
 692                  }
 693                  if (($index <= 0) || ($index > count($printAreas))) {
 694                      throw new PhpSpreadsheetException('Invalid index for setting print range.');
 695                  }
 696                  $printAreas[$index - 1] = $value;
 697                  $this->printArea = implode(',', $printAreas);
 698              }
 699          } elseif ($method == self::SETPRINTRANGE_INSERT) {
 700              if ($index == 0) {
 701                  $this->printArea = $this->printArea ? ($this->printArea . ',' . $value) : $value;
 702              } else {
 703                  $printAreas = explode(',', $this->printArea);
 704                  if ($index < 0) {
 705                      $index = abs($index) - 1;
 706                  }
 707                  if ($index > count($printAreas)) {
 708                      throw new PhpSpreadsheetException('Invalid index for setting print range.');
 709                  }
 710                  $printAreas = array_merge(array_slice($printAreas, 0, $index), [$value], array_slice($printAreas, $index));
 711                  $this->printArea = implode(',', $printAreas);
 712              }
 713          } else {
 714              throw new PhpSpreadsheetException('Invalid method for setting print range.');
 715          }
 716  
 717          return $this;
 718      }
 719  
 720      /**
 721       * Add a new print area (e.g. 'A1:D10' or 'A1:D10,G5:M20') to the list of print areas.
 722       *
 723       * @param string $value
 724       * @param int $index Identifier for a specific print area range allowing several ranges to be set
 725       *                            A positive index will insert after that indexed entry in the print areas list, while a
 726       *                                negative index will insert before the indexed entry.
 727       *                                Specifying an index value of 0, will always append the new print range at the end of the
 728       *                                list.
 729       *                            Print areas are numbered from 1
 730       *
 731       * @return $this
 732       */
 733      public function addPrintArea($value, $index = -1)
 734      {
 735          return $this->setPrintArea($value, $index, self::SETPRINTRANGE_INSERT);
 736      }
 737  
 738      /**
 739       * Set print area.
 740       *
 741       * @param int $column1 Column 1
 742       * @param int $row1 Row 1
 743       * @param int $column2 Column 2
 744       * @param int $row2 Row 2
 745       * @param int $index Identifier for a specific print area range allowing several ranges to be set
 746       *                                When the method is "O"verwrite, then a positive integer index will overwrite that indexed
 747       *                                    entry in the print areas list; a negative index value will identify which entry to
 748       *                                    overwrite working backward through the print area to the list, with the last entry as -1.
 749       *                                    Specifying an index value of 0, will overwrite <b>all</b> existing print ranges.
 750       *                                When the method is "I"nsert, then a positive index will insert after that indexed entry in
 751       *                                    the print areas list, while a negative index will insert before the indexed entry.
 752       *                                    Specifying an index value of 0, will always append the new print range at the end of the
 753       *                                    list.
 754       *                                Print areas are numbered from 1
 755       * @param string $method Determines the method used when setting multiple print areas
 756       *                                Default behaviour, or the "O" method, overwrites existing print area
 757       *                                The "I" method, inserts the new print area before any specified index, or at the end of the list
 758       *
 759       * @return $this
 760       */
 761      public function setPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
 762      {
 763          return $this->setPrintArea(
 764              Coordinate::stringFromColumnIndex($column1) . $row1 . ':' . Coordinate::stringFromColumnIndex($column2) . $row2,
 765              $index,
 766              $method
 767          );
 768      }
 769  
 770      /**
 771       * Add a new print area to the list of print areas.
 772       *
 773       * @param int $column1 Start Column for the print area
 774       * @param int $row1 Start Row for the print area
 775       * @param int $column2 End Column for the print area
 776       * @param int $row2 End Row for the print area
 777       * @param int $index Identifier for a specific print area range allowing several ranges to be set
 778       *                                A positive index will insert after that indexed entry in the print areas list, while a
 779       *                                    negative index will insert before the indexed entry.
 780       *                                    Specifying an index value of 0, will always append the new print range at the end of the
 781       *                                    list.
 782       *                                Print areas are numbered from 1
 783       *
 784       * @return $this
 785       */
 786      public function addPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = -1)
 787      {
 788          return $this->setPrintArea(
 789              Coordinate::stringFromColumnIndex($column1) . $row1 . ':' . Coordinate::stringFromColumnIndex($column2) . $row2,
 790              $index,
 791              self::SETPRINTRANGE_INSERT
 792          );
 793      }
 794  
 795      /**
 796       * Get first page number.
 797       *
 798       * @return int
 799       */
 800      public function getFirstPageNumber()
 801      {
 802          return $this->firstPageNumber;
 803      }
 804  
 805      /**
 806       * Set first page number.
 807       *
 808       * @param int $value
 809       *
 810       * @return $this
 811       */
 812      public function setFirstPageNumber($value)
 813      {
 814          $this->firstPageNumber = $value;
 815  
 816          return $this;
 817      }
 818  
 819      /**
 820       * Reset first page number.
 821       *
 822       * @return $this
 823       */
 824      public function resetFirstPageNumber()
 825      {
 826          return $this->setFirstPageNumber(null);
 827      }
 828  
 829      public function getPageOrder(): string
 830      {
 831          return $this->pageOrder;
 832      }
 833  
 834      public function setPageOrder(?string $pageOrder): self
 835      {
 836          if ($pageOrder === null || $pageOrder === self::PAGEORDER_DOWN_THEN_OVER || $pageOrder === self::PAGEORDER_OVER_THEN_DOWN) {
 837              $this->pageOrder = $pageOrder ?? self::PAGEORDER_DOWN_THEN_OVER;
 838          }
 839  
 840          return $this;
 841      }
 842  
 843      /**
 844       * Implement PHP __clone to create a deep clone, not just a shallow copy.
 845       */
 846      public function __clone()
 847      {
 848          $vars = get_object_vars($this);
 849          foreach ($vars as $key => $value) {
 850              if (is_object($value)) {
 851                  $this->$key = clone $value;
 852              } else {
 853                  $this->$key = $value;
 854              }
 855          }
 856      }
 857  }