Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

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

   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 default.
 164       *
 165       * @var int
 166       */
 167      private static $paperSizeDefault = self::PAPERSIZE_LETTER;
 168  
 169      /**
 170       * Paper size.
 171       *
 172       * @var ?int
 173       */
 174      private $paperSize;
 175  
 176      /**
 177       * Orientation default.
 178       *
 179       * @var string
 180       */
 181      private static $orientationDefault = self::ORIENTATION_DEFAULT;
 182  
 183      /**
 184       * Orientation.
 185       *
 186       * @var string
 187       */
 188      private $orientation;
 189  
 190      /**
 191       * Scale (Print Scale).
 192       *
 193       * Print scaling. Valid values range from 10 to 400
 194       * This setting is overridden when fitToWidth and/or fitToHeight are in use
 195       *
 196       * @var null|int
 197       */
 198      private $scale = 100;
 199  
 200      /**
 201       * Fit To Page
 202       * Whether scale or fitToWith / fitToHeight applies.
 203       *
 204       * @var bool
 205       */
 206      private $fitToPage = false;
 207  
 208      /**
 209       * Fit To Height
 210       * Number of vertical pages to fit on.
 211       *
 212       * @var null|int
 213       */
 214      private $fitToHeight = 1;
 215  
 216      /**
 217       * Fit To Width
 218       * Number of horizontal pages to fit on.
 219       *
 220       * @var null|int
 221       */
 222      private $fitToWidth = 1;
 223  
 224      /**
 225       * Columns to repeat at left.
 226       *
 227       * @var array Containing start column and end column, empty array if option unset
 228       */
 229      private $columnsToRepeatAtLeft = ['', ''];
 230  
 231      /**
 232       * Rows to repeat at top.
 233       *
 234       * @var array Containing start row number and end row number, empty array if option unset
 235       */
 236      private $rowsToRepeatAtTop = [0, 0];
 237  
 238      /**
 239       * Center page horizontally.
 240       *
 241       * @var bool
 242       */
 243      private $horizontalCentered = false;
 244  
 245      /**
 246       * Center page vertically.
 247       *
 248       * @var bool
 249       */
 250      private $verticalCentered = false;
 251  
 252      /**
 253       * Print area.
 254       *
 255       * @var null|string
 256       */
 257      private $printArea;
 258  
 259      /**
 260       * First page number.
 261       *
 262       * @var ?int
 263       */
 264      private $firstPageNumber;
 265  
 266      /** @var string */
 267      private $pageOrder = self::PAGEORDER_DOWN_THEN_OVER;
 268  
 269      /**
 270       * Create a new PageSetup.
 271       */
 272      public function __construct()
 273      {
 274          $this->orientation = self::$orientationDefault;
 275      }
 276  
 277      /**
 278       * Get Paper Size.
 279       *
 280       * @return int
 281       */
 282      public function getPaperSize()
 283      {
 284          return $this->paperSize ?? self::$paperSizeDefault;
 285      }
 286  
 287      /**
 288       * Set Paper Size.
 289       *
 290       * @param int $paperSize see self::PAPERSIZE_*
 291       *
 292       * @return $this
 293       */
 294      public function setPaperSize($paperSize)
 295      {
 296          $this->paperSize = $paperSize;
 297  
 298          return $this;
 299      }
 300  
 301      /**
 302       * Get Paper Size default.
 303       */
 304      public static function getPaperSizeDefault(): int
 305      {
 306          return self::$paperSizeDefault;
 307      }
 308  
 309      /**
 310       * Set Paper Size Default.
 311       */
 312      public static function setPaperSizeDefault(int $paperSize): void
 313      {
 314          self::$paperSizeDefault = $paperSize;
 315      }
 316  
 317      /**
 318       * Get Orientation.
 319       *
 320       * @return string
 321       */
 322      public function getOrientation()
 323      {
 324          return $this->orientation;
 325      }
 326  
 327      /**
 328       * Set Orientation.
 329       *
 330       * @param string $orientation see self::ORIENTATION_*
 331       *
 332       * @return $this
 333       */
 334      public function setOrientation($orientation)
 335      {
 336          if ($orientation === self::ORIENTATION_LANDSCAPE || $orientation === self::ORIENTATION_PORTRAIT || $orientation === self::ORIENTATION_DEFAULT) {
 337              $this->orientation = $orientation;
 338          }
 339  
 340          return $this;
 341      }
 342  
 343      public static function getOrientationDefault(): string
 344      {
 345          return self::$orientationDefault;
 346      }
 347  
 348      public static function setOrientationDefault(string $orientation): void
 349      {
 350          if ($orientation === self::ORIENTATION_LANDSCAPE || $orientation === self::ORIENTATION_PORTRAIT || $orientation === self::ORIENTATION_DEFAULT) {
 351              self::$orientationDefault = $orientation;
 352          }
 353      }
 354  
 355      /**
 356       * Get Scale.
 357       *
 358       * @return null|int
 359       */
 360      public function getScale()
 361      {
 362          return $this->scale;
 363      }
 364  
 365      /**
 366       * Set Scale.
 367       * Print scaling. Valid values range from 10 to 400
 368       * This setting is overridden when fitToWidth and/or fitToHeight are in use.
 369       *
 370       * @param null|int $scale
 371       * @param bool $update Update fitToPage so scaling applies rather than fitToHeight / fitToWidth
 372       *
 373       * @return $this
 374       */
 375      public function setScale($scale, $update = true)
 376      {
 377          // Microsoft Office Excel 2007 only allows setting a scale between 10 and 400 via the user interface,
 378          // but it is apparently still able to handle any scale >= 0, where 0 results in 100
 379          if ($scale === null || $scale >= 0) {
 380              $this->scale = $scale;
 381              if ($update) {
 382                  $this->fitToPage = false;
 383              }
 384          } else {
 385              throw new PhpSpreadsheetException('Scale must not be negative');
 386          }
 387  
 388          return $this;
 389      }
 390  
 391      /**
 392       * Get Fit To Page.
 393       *
 394       * @return bool
 395       */
 396      public function getFitToPage()
 397      {
 398          return $this->fitToPage;
 399      }
 400  
 401      /**
 402       * Set Fit To Page.
 403       *
 404       * @param bool $fitToPage
 405       *
 406       * @return $this
 407       */
 408      public function setFitToPage($fitToPage)
 409      {
 410          $this->fitToPage = $fitToPage;
 411  
 412          return $this;
 413      }
 414  
 415      /**
 416       * Get Fit To Height.
 417       *
 418       * @return null|int
 419       */
 420      public function getFitToHeight()
 421      {
 422          return $this->fitToHeight;
 423      }
 424  
 425      /**
 426       * Set Fit To Height.
 427       *
 428       * @param null|int $fitToHeight
 429       * @param bool $update Update fitToPage so it applies rather than scaling
 430       *
 431       * @return $this
 432       */
 433      public function setFitToHeight($fitToHeight, $update = true)
 434      {
 435          $this->fitToHeight = $fitToHeight;
 436          if ($update) {
 437              $this->fitToPage = true;
 438          }
 439  
 440          return $this;
 441      }
 442  
 443      /**
 444       * Get Fit To Width.
 445       *
 446       * @return null|int
 447       */
 448      public function getFitToWidth()
 449      {
 450          return $this->fitToWidth;
 451      }
 452  
 453      /**
 454       * Set Fit To Width.
 455       *
 456       * @param null|int $value
 457       * @param bool $update Update fitToPage so it applies rather than scaling
 458       *
 459       * @return $this
 460       */
 461      public function setFitToWidth($value, $update = true)
 462      {
 463          $this->fitToWidth = $value;
 464          if ($update) {
 465              $this->fitToPage = true;
 466          }
 467  
 468          return $this;
 469      }
 470  
 471      /**
 472       * Is Columns to repeat at left set?
 473       *
 474       * @return bool
 475       */
 476      public function isColumnsToRepeatAtLeftSet()
 477      {
 478          if (!empty($this->columnsToRepeatAtLeft)) {
 479              if ($this->columnsToRepeatAtLeft[0] != '' && $this->columnsToRepeatAtLeft[1] != '') {
 480                  return true;
 481              }
 482          }
 483  
 484          return false;
 485      }
 486  
 487      /**
 488       * Get Columns to repeat at left.
 489       *
 490       * @return array Containing start column and end column, empty array if option unset
 491       */
 492      public function getColumnsToRepeatAtLeft()
 493      {
 494          return $this->columnsToRepeatAtLeft;
 495      }
 496  
 497      /**
 498       * Set Columns to repeat at left.
 499       *
 500       * @param array $columnsToRepeatAtLeft Containing start column and end column, empty array if option unset
 501       *
 502       * @return $this
 503       */
 504      public function setColumnsToRepeatAtLeft(array $columnsToRepeatAtLeft)
 505      {
 506          $this->columnsToRepeatAtLeft = $columnsToRepeatAtLeft;
 507  
 508          return $this;
 509      }
 510  
 511      /**
 512       * Set Columns to repeat at left by start and end.
 513       *
 514       * @param string $start eg: 'A'
 515       * @param string $end eg: 'B'
 516       *
 517       * @return $this
 518       */
 519      public function setColumnsToRepeatAtLeftByStartAndEnd($start, $end)
 520      {
 521          $this->columnsToRepeatAtLeft = [$start, $end];
 522  
 523          return $this;
 524      }
 525  
 526      /**
 527       * Is Rows to repeat at top set?
 528       *
 529       * @return bool
 530       */
 531      public function isRowsToRepeatAtTopSet()
 532      {
 533          if (!empty($this->rowsToRepeatAtTop)) {
 534              if ($this->rowsToRepeatAtTop[0] != 0 && $this->rowsToRepeatAtTop[1] != 0) {
 535                  return true;
 536              }
 537          }
 538  
 539          return false;
 540      }
 541  
 542      /**
 543       * Get Rows to repeat at top.
 544       *
 545       * @return array Containing start column and end column, empty array if option unset
 546       */
 547      public function getRowsToRepeatAtTop()
 548      {
 549          return $this->rowsToRepeatAtTop;
 550      }
 551  
 552      /**
 553       * Set Rows to repeat at top.
 554       *
 555       * @param array $rowsToRepeatAtTop Containing start column and end column, empty array if option unset
 556       *
 557       * @return $this
 558       */
 559      public function setRowsToRepeatAtTop(array $rowsToRepeatAtTop)
 560      {
 561          $this->rowsToRepeatAtTop = $rowsToRepeatAtTop;
 562  
 563          return $this;
 564      }
 565  
 566      /**
 567       * Set Rows to repeat at top by start and end.
 568       *
 569       * @param int $start eg: 1
 570       * @param int $end eg: 1
 571       *
 572       * @return $this
 573       */
 574      public function setRowsToRepeatAtTopByStartAndEnd($start, $end)
 575      {
 576          $this->rowsToRepeatAtTop = [$start, $end];
 577  
 578          return $this;
 579      }
 580  
 581      /**
 582       * Get center page horizontally.
 583       *
 584       * @return bool
 585       */
 586      public function getHorizontalCentered()
 587      {
 588          return $this->horizontalCentered;
 589      }
 590  
 591      /**
 592       * Set center page horizontally.
 593       *
 594       * @param bool $value
 595       *
 596       * @return $this
 597       */
 598      public function setHorizontalCentered($value)
 599      {
 600          $this->horizontalCentered = $value;
 601  
 602          return $this;
 603      }
 604  
 605      /**
 606       * Get center page vertically.
 607       *
 608       * @return bool
 609       */
 610      public function getVerticalCentered()
 611      {
 612          return $this->verticalCentered;
 613      }
 614  
 615      /**
 616       * Set center page vertically.
 617       *
 618       * @param bool $value
 619       *
 620       * @return $this
 621       */
 622      public function setVerticalCentered($value)
 623      {
 624          $this->verticalCentered = $value;
 625  
 626          return $this;
 627      }
 628  
 629      /**
 630       * Get print area.
 631       *
 632       * @param int $index Identifier for a specific print area range if several ranges have been set
 633       *                            Default behaviour, or a index value of 0, will return all ranges as a comma-separated string
 634       *                            Otherwise, the specific range identified by the value of $index will be returned
 635       *                            Print areas are numbered from 1
 636       *
 637       * @return string
 638       */
 639      public function getPrintArea($index = 0)
 640      {
 641          if ($index == 0) {
 642              return (string) $this->printArea;
 643          }
 644          $printAreas = explode(',', (string) $this->printArea);
 645          if (isset($printAreas[$index - 1])) {
 646              return $printAreas[$index - 1];
 647          }
 648  
 649          throw new PhpSpreadsheetException('Requested Print Area does not exist');
 650      }
 651  
 652      /**
 653       * Is print area set?
 654       *
 655       * @param int $index Identifier for a specific print area range if several ranges have been set
 656       *                            Default behaviour, or an index value of 0, will identify whether any print range is set
 657       *                            Otherwise, existence of the range identified by the value of $index will be returned
 658       *                            Print areas are numbered from 1
 659       *
 660       * @return bool
 661       */
 662      public function isPrintAreaSet($index = 0)
 663      {
 664          if ($index == 0) {
 665              return $this->printArea !== null;
 666          }
 667          $printAreas = explode(',', (string) $this->printArea);
 668  
 669          return isset($printAreas[$index - 1]);
 670      }
 671  
 672      /**
 673       * Clear a print area.
 674       *
 675       * @param int $index Identifier for a specific print area range if several ranges have been set
 676       *                            Default behaviour, or an index value of 0, will clear all print ranges that are set
 677       *                            Otherwise, the range identified by the value of $index will be removed from the series
 678       *                            Print areas are numbered from 1
 679       *
 680       * @return $this
 681       */
 682      public function clearPrintArea($index = 0)
 683      {
 684          if ($index == 0) {
 685              $this->printArea = null;
 686          } else {
 687              $printAreas = explode(',', (string) $this->printArea);
 688              if (isset($printAreas[$index - 1])) {
 689                  unset($printAreas[$index - 1]);
 690                  $this->printArea = implode(',', $printAreas);
 691              }
 692          }
 693  
 694          return $this;
 695      }
 696  
 697      /**
 698       * Set print area. e.g. 'A1:D10' or 'A1:D10,G5:M20'.
 699       *
 700       * @param string $value
 701       * @param int $index Identifier for a specific print area range allowing several ranges to be set
 702       *                            When the method is "O"verwrite, then a positive integer index will overwrite that indexed
 703       *                                entry in the print areas list; a negative index value will identify which entry to
 704       *                                overwrite working bacward through the print area to the list, with the last entry as -1.
 705       *                                Specifying an index value of 0, will overwrite <b>all</b> existing print ranges.
 706       *                            When the method is "I"nsert, then a positive index will insert after that indexed entry in
 707       *                                the print areas list, while a negative index will insert before the indexed entry.
 708       *                                Specifying an index value of 0, will always append the new print range at the end of the
 709       *                                list.
 710       *                            Print areas are numbered from 1
 711       * @param string $method Determines the method used when setting multiple print areas
 712       *                            Default behaviour, or the "O" method, overwrites existing print area
 713       *                            The "I" method, inserts the new print area before any specified index, or at the end of the list
 714       *
 715       * @return $this
 716       */
 717      public function setPrintArea($value, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
 718      {
 719          if (strpos($value, '!') !== false) {
 720              throw new PhpSpreadsheetException('Cell coordinate must not specify a worksheet.');
 721          } elseif (strpos($value, ':') === false) {
 722              throw new PhpSpreadsheetException('Cell coordinate must be a range of cells.');
 723          } elseif (strpos($value, '$') !== false) {
 724              throw new PhpSpreadsheetException('Cell coordinate must not be absolute.');
 725          }
 726          $value = strtoupper($value);
 727          if (!$this->printArea) {
 728              $index = 0;
 729          }
 730  
 731          if ($method == self::SETPRINTRANGE_OVERWRITE) {
 732              if ($index == 0) {
 733                  $this->printArea = $value;
 734              } else {
 735                  $printAreas = explode(',', (string) $this->printArea);
 736                  if ($index < 0) {
 737                      $index = count($printAreas) - abs($index) + 1;
 738                  }
 739                  if (($index <= 0) || ($index > count($printAreas))) {
 740                      throw new PhpSpreadsheetException('Invalid index for setting print range.');
 741                  }
 742                  $printAreas[$index - 1] = $value;
 743                  $this->printArea = implode(',', $printAreas);
 744              }
 745          } elseif ($method == self::SETPRINTRANGE_INSERT) {
 746              if ($index == 0) {
 747                  $this->printArea = $this->printArea ? ($this->printArea . ',' . $value) : $value;
 748              } else {
 749                  $printAreas = explode(',', (string) $this->printArea);
 750                  if ($index < 0) {
 751                      $index = (int) abs($index) - 1;
 752                  }
 753                  if ($index > count($printAreas)) {
 754                      throw new PhpSpreadsheetException('Invalid index for setting print range.');
 755                  }
 756                  $printAreas = array_merge(array_slice($printAreas, 0, $index), [$value], array_slice($printAreas, $index));
 757                  $this->printArea = implode(',', $printAreas);
 758              }
 759          } else {
 760              throw new PhpSpreadsheetException('Invalid method for setting print range.');
 761          }
 762  
 763          return $this;
 764      }
 765  
 766      /**
 767       * Add a new print area (e.g. 'A1:D10' or 'A1:D10,G5:M20') to the list of print areas.
 768       *
 769       * @param string $value
 770       * @param int $index Identifier for a specific print area range allowing several ranges to be set
 771       *                            A positive index will insert after that indexed entry in the print areas list, while a
 772       *                                negative index will insert before the indexed entry.
 773       *                                Specifying an index value of 0, will always append the new print range at the end of the
 774       *                                list.
 775       *                            Print areas are numbered from 1
 776       *
 777       * @return $this
 778       */
 779      public function addPrintArea($value, $index = -1)
 780      {
 781          return $this->setPrintArea($value, $index, self::SETPRINTRANGE_INSERT);
 782      }
 783  
 784      /**
 785       * Set print area.
 786       *
 787       * @param int $column1 Column 1
 788       * @param int $row1 Row 1
 789       * @param int $column2 Column 2
 790       * @param int $row2 Row 2
 791       * @param int $index Identifier for a specific print area range allowing several ranges to be set
 792       *                                When the method is "O"verwrite, then a positive integer index will overwrite that indexed
 793       *                                    entry in the print areas list; a negative index value will identify which entry to
 794       *                                    overwrite working backward through the print area to the list, with the last entry as -1.
 795       *                                    Specifying an index value of 0, will overwrite <b>all</b> existing print ranges.
 796       *                                When the method is "I"nsert, then a positive index will insert after that indexed entry in
 797       *                                    the print areas list, while a negative index will insert before the indexed entry.
 798       *                                    Specifying an index value of 0, will always append the new print range at the end of the
 799       *                                    list.
 800       *                                Print areas are numbered from 1
 801       * @param string $method Determines the method used when setting multiple print areas
 802       *                                Default behaviour, or the "O" method, overwrites existing print area
 803       *                                The "I" method, inserts the new print area before any specified index, or at the end of the list
 804       *
 805       * @return $this
 806       */
 807      public function setPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
 808      {
 809          return $this->setPrintArea(
 810              Coordinate::stringFromColumnIndex($column1) . $row1 . ':' . Coordinate::stringFromColumnIndex($column2) . $row2,
 811              $index,
 812              $method
 813          );
 814      }
 815  
 816      /**
 817       * Add a new print area to the list of print areas.
 818       *
 819       * @param int $column1 Start Column for the print area
 820       * @param int $row1 Start Row for the print area
 821       * @param int $column2 End Column for the print area
 822       * @param int $row2 End Row for the print area
 823       * @param int $index Identifier for a specific print area range allowing several ranges to be set
 824       *                                A positive index will insert after that indexed entry in the print areas list, while a
 825       *                                    negative index will insert before the indexed entry.
 826       *                                    Specifying an index value of 0, will always append the new print range at the end of the
 827       *                                    list.
 828       *                                Print areas are numbered from 1
 829       *
 830       * @return $this
 831       */
 832      public function addPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = -1)
 833      {
 834          return $this->setPrintArea(
 835              Coordinate::stringFromColumnIndex($column1) . $row1 . ':' . Coordinate::stringFromColumnIndex($column2) . $row2,
 836              $index,
 837              self::SETPRINTRANGE_INSERT
 838          );
 839      }
 840  
 841      /**
 842       * Get first page number.
 843       *
 844       * @return ?int
 845       */
 846      public function getFirstPageNumber()
 847      {
 848          return $this->firstPageNumber;
 849      }
 850  
 851      /**
 852       * Set first page number.
 853       *
 854       * @param ?int $value
 855       *
 856       * @return $this
 857       */
 858      public function setFirstPageNumber($value)
 859      {
 860          $this->firstPageNumber = $value;
 861  
 862          return $this;
 863      }
 864  
 865      /**
 866       * Reset first page number.
 867       *
 868       * @return $this
 869       */
 870      public function resetFirstPageNumber()
 871      {
 872          return $this->setFirstPageNumber(null);
 873      }
 874  
 875      public function getPageOrder(): string
 876      {
 877          return $this->pageOrder;
 878      }
 879  
 880      public function setPageOrder(?string $pageOrder): self
 881      {
 882          if ($pageOrder === null || $pageOrder === self::PAGEORDER_DOWN_THEN_OVER || $pageOrder === self::PAGEORDER_OVER_THEN_DOWN) {
 883              $this->pageOrder = $pageOrder ?? self::PAGEORDER_DOWN_THEN_OVER;
 884          }
 885  
 886          return $this;
 887      }
 888  
 889      /**
 890       * Implement PHP __clone to create a deep clone, not just a shallow copy.
 891       */
 892      public function __clone()
 893      {
 894          $vars = get_object_vars($this);
 895          foreach ($vars as $key => $value) {
 896              if (is_object($value)) {
 897                  $this->$key = clone $value;
 898              } else {
 899                  $this->$key = $value;
 900              }
 901          }
 902      }
 903  }