Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 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 (is_array($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 (is_array($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 $this->printArea;
 643          }
 644          /** @phpstan-ignore-next-line */
 645          $printAreas = explode(',', $this->printArea);
 646          if (isset($printAreas[$index - 1])) {
 647              return $printAreas[$index - 1];
 648          }
 649  
 650          throw new PhpSpreadsheetException('Requested Print Area does not exist');
 651      }
 652  
 653      /**
 654       * Is print area set?
 655       *
 656       * @param int $index Identifier for a specific print area range if several ranges have been set
 657       *                            Default behaviour, or an index value of 0, will identify whether any print range is set
 658       *                            Otherwise, existence of the range identified by the value of $index will be returned
 659       *                            Print areas are numbered from 1
 660       *
 661       * @return bool
 662       */
 663      public function isPrintAreaSet($index = 0)
 664      {
 665          if ($index == 0) {
 666              return $this->printArea !== null;
 667          }
 668          /** @phpstan-ignore-next-line */
 669          $printAreas = explode(',', $this->printArea);
 670  
 671          return isset($printAreas[$index - 1]);
 672      }
 673  
 674      /**
 675       * Clear a print area.
 676       *
 677       * @param int $index Identifier for a specific print area range if several ranges have been set
 678       *                            Default behaviour, or an index value of 0, will clear all print ranges that are set
 679       *                            Otherwise, the range identified by the value of $index will be removed from the series
 680       *                            Print areas are numbered from 1
 681       *
 682       * @return $this
 683       */
 684      public function clearPrintArea($index = 0)
 685      {
 686          if ($index == 0) {
 687              $this->printArea = null;
 688          } else {
 689              /** @phpstan-ignore-next-line */
 690              $printAreas = explode(',', $this->printArea);
 691              if (isset($printAreas[$index - 1])) {
 692                  unset($printAreas[$index - 1]);
 693                  $this->printArea = implode(',', $printAreas);
 694              }
 695          }
 696  
 697          return $this;
 698      }
 699  
 700      /**
 701       * Set print area. e.g. 'A1:D10' or 'A1:D10,G5:M20'.
 702       *
 703       * @param string $value
 704       * @param int $index Identifier for a specific print area range allowing several ranges to be set
 705       *                            When the method is "O"verwrite, then a positive integer index will overwrite that indexed
 706       *                                entry in the print areas list; a negative index value will identify which entry to
 707       *                                overwrite working bacward through the print area to the list, with the last entry as -1.
 708       *                                Specifying an index value of 0, will overwrite <b>all</b> existing print ranges.
 709       *                            When the method is "I"nsert, then a positive index will insert after that indexed entry in
 710       *                                the print areas list, while a negative index will insert before the indexed entry.
 711       *                                Specifying an index value of 0, will always append the new print range at the end of the
 712       *                                list.
 713       *                            Print areas are numbered from 1
 714       * @param string $method Determines the method used when setting multiple print areas
 715       *                            Default behaviour, or the "O" method, overwrites existing print area
 716       *                            The "I" method, inserts the new print area before any specified index, or at the end of the list
 717       *
 718       * @return $this
 719       */
 720      public function setPrintArea($value, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
 721      {
 722          if (strpos($value, '!') !== false) {
 723              throw new PhpSpreadsheetException('Cell coordinate must not specify a worksheet.');
 724          } elseif (strpos($value, ':') === false) {
 725              throw new PhpSpreadsheetException('Cell coordinate must be a range of cells.');
 726          } elseif (strpos($value, '$') !== false) {
 727              throw new PhpSpreadsheetException('Cell coordinate must not be absolute.');
 728          }
 729          $value = strtoupper($value);
 730          if (!$this->printArea) {
 731              $index = 0;
 732          }
 733  
 734          if ($method == self::SETPRINTRANGE_OVERWRITE) {
 735              if ($index == 0) {
 736                  $this->printArea = $value;
 737              } else {
 738                  /** @phpstan-ignore-next-line */
 739                  $printAreas = explode(',', $this->printArea);
 740                  if ($index < 0) {
 741                      $index = count($printAreas) - abs($index) + 1;
 742                  }
 743                  if (($index <= 0) || ($index > count($printAreas))) {
 744                      throw new PhpSpreadsheetException('Invalid index for setting print range.');
 745                  }
 746                  $printAreas[$index - 1] = $value;
 747                  $this->printArea = implode(',', $printAreas);
 748              }
 749          } elseif ($method == self::SETPRINTRANGE_INSERT) {
 750              if ($index == 0) {
 751                  $this->printArea = $this->printArea ? ($this->printArea . ',' . $value) : $value;
 752              } else {
 753                  /** @phpstan-ignore-next-line */
 754                  $printAreas = explode(',', $this->printArea);
 755                  if ($index < 0) {
 756                      $index = abs($index) - 1;
 757                  }
 758                  if ($index > count($printAreas)) {
 759                      throw new PhpSpreadsheetException('Invalid index for setting print range.');
 760                  }
 761                  $printAreas = array_merge(array_slice($printAreas, 0, $index), [$value], array_slice($printAreas, $index));
 762                  $this->printArea = implode(',', $printAreas);
 763              }
 764          } else {
 765              throw new PhpSpreadsheetException('Invalid method for setting print range.');
 766          }
 767  
 768          return $this;
 769      }
 770  
 771      /**
 772       * Add a new print area (e.g. 'A1:D10' or 'A1:D10,G5:M20') to the list of print areas.
 773       *
 774       * @param string $value
 775       * @param int $index Identifier for a specific print area range allowing several ranges to be set
 776       *                            A positive index will insert after that indexed entry in the print areas list, while a
 777       *                                negative index will insert before the indexed entry.
 778       *                                Specifying an index value of 0, will always append the new print range at the end of the
 779       *                                list.
 780       *                            Print areas are numbered from 1
 781       *
 782       * @return $this
 783       */
 784      public function addPrintArea($value, $index = -1)
 785      {
 786          return $this->setPrintArea($value, $index, self::SETPRINTRANGE_INSERT);
 787      }
 788  
 789      /**
 790       * Set print area.
 791       *
 792       * @param int $column1 Column 1
 793       * @param int $row1 Row 1
 794       * @param int $column2 Column 2
 795       * @param int $row2 Row 2
 796       * @param int $index Identifier for a specific print area range allowing several ranges to be set
 797       *                                When the method is "O"verwrite, then a positive integer index will overwrite that indexed
 798       *                                    entry in the print areas list; a negative index value will identify which entry to
 799       *                                    overwrite working backward through the print area to the list, with the last entry as -1.
 800       *                                    Specifying an index value of 0, will overwrite <b>all</b> existing print ranges.
 801       *                                When the method is "I"nsert, then a positive index will insert after that indexed entry in
 802       *                                    the print areas list, while a negative index will insert before the indexed entry.
 803       *                                    Specifying an index value of 0, will always append the new print range at the end of the
 804       *                                    list.
 805       *                                Print areas are numbered from 1
 806       * @param string $method Determines the method used when setting multiple print areas
 807       *                                Default behaviour, or the "O" method, overwrites existing print area
 808       *                                The "I" method, inserts the new print area before any specified index, or at the end of the list
 809       *
 810       * @return $this
 811       */
 812      public function setPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
 813      {
 814          return $this->setPrintArea(
 815              Coordinate::stringFromColumnIndex($column1) . $row1 . ':' . Coordinate::stringFromColumnIndex($column2) . $row2,
 816              $index,
 817              $method
 818          );
 819      }
 820  
 821      /**
 822       * Add a new print area to the list of print areas.
 823       *
 824       * @param int $column1 Start Column for the print area
 825       * @param int $row1 Start Row for the print area
 826       * @param int $column2 End Column for the print area
 827       * @param int $row2 End Row for the print area
 828       * @param int $index Identifier for a specific print area range allowing several ranges to be set
 829       *                                A positive index will insert after that indexed entry in the print areas list, while a
 830       *                                    negative index will insert before the indexed entry.
 831       *                                    Specifying an index value of 0, will always append the new print range at the end of the
 832       *                                    list.
 833       *                                Print areas are numbered from 1
 834       *
 835       * @return $this
 836       */
 837      public function addPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = -1)
 838      {
 839          return $this->setPrintArea(
 840              Coordinate::stringFromColumnIndex($column1) . $row1 . ':' . Coordinate::stringFromColumnIndex($column2) . $row2,
 841              $index,
 842              self::SETPRINTRANGE_INSERT
 843          );
 844      }
 845  
 846      /**
 847       * Get first page number.
 848       *
 849       * @return ?int
 850       */
 851      public function getFirstPageNumber()
 852      {
 853          return $this->firstPageNumber;
 854      }
 855  
 856      /**
 857       * Set first page number.
 858       *
 859       * @param ?int $value
 860       *
 861       * @return $this
 862       */
 863      public function setFirstPageNumber($value)
 864      {
 865          $this->firstPageNumber = $value;
 866  
 867          return $this;
 868      }
 869  
 870      /**
 871       * Reset first page number.
 872       *
 873       * @return $this
 874       */
 875      public function resetFirstPageNumber()
 876      {
 877          return $this->setFirstPageNumber(null);
 878      }
 879  
 880      public function getPageOrder(): string
 881      {
 882          return $this->pageOrder;
 883      }
 884  
 885      public function setPageOrder(?string $pageOrder): self
 886      {
 887          if ($pageOrder === null || $pageOrder === self::PAGEORDER_DOWN_THEN_OVER || $pageOrder === self::PAGEORDER_OVER_THEN_DOWN) {
 888              $this->pageOrder = $pageOrder ?? self::PAGEORDER_DOWN_THEN_OVER;
 889          }
 890  
 891          return $this;
 892      }
 893  
 894      /**
 895       * Implement PHP __clone to create a deep clone, not just a shallow copy.
 896       */
 897      public function __clone()
 898      {
 899          $vars = get_object_vars($this);
 900          foreach ($vars as $key => $value) {
 901              if (is_object($value)) {
 902                  $this->$key = clone $value;
 903              } else {
 904                  $this->$key = $value;
 905              }
 906          }
 907      }
 908  }