Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
1 <?php 2 /** 3 * This file is part of FPDI 4 * 5 * @package setasign\Fpdi 6 * @copyright Copyright (c) 2019 Setasign - Jan Slabon (https://www.setasign.com) 7 * @license http://opensource.org/licenses/mit-license The MIT License 8 */ 9 10 namespace setasign\Fpdi\PdfReader; 11 12 use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException; 13 use setasign\Fpdi\PdfParser\PdfParser; 14 use setasign\Fpdi\PdfParser\PdfParserException; 15 use setasign\Fpdi\PdfParser\Type\PdfArray; 16 use setasign\Fpdi\PdfParser\Type\PdfDictionary; 17 use setasign\Fpdi\PdfParser\Type\PdfIndirectObjectReference; 18 use setasign\Fpdi\PdfParser\Type\PdfNumeric; 19 use setasign\Fpdi\PdfParser\Type\PdfType; 20 use setasign\Fpdi\PdfParser\Type\PdfTypeException; 21 22 /** 23 * A PDF reader class 24 * 25 * @package setasign\Fpdi\PdfReader 26 */ 27 class PdfReader 28 { 29 /** 30 * @var PdfParser 31 */ 32 protected $parser; 33 34 /** 35 * @var int 36 */ 37 protected $pageCount; 38 39 /** 40 * Indirect objects of resolved pages. 41 * 42 * @var PdfIndirectObjectReference[] 43 */ 44 protected $pages = []; 45 46 /** 47 * PdfReader constructor. 48 * 49 * @param PdfParser $parser 50 */ 51 public function __construct(PdfParser $parser) 52 { 53 $this->parser = $parser; 54 } 55 56 /** 57 * PdfReader destructor. 58 */ 59 public function __destruct() 60 { 61 if ($this->parser !== null) { 62 /** @noinspection PhpInternalEntityUsedInspection */ 63 $this->parser->cleanUp(); 64 } 65 } 66 67 /** 68 * Get the pdf parser instance. 69 * 70 * @return PdfParser 71 */ 72 public function getParser() 73 { 74 return $this->parser; 75 } 76 77 /** 78 * Get the PDF version. 79 * 80 * @return string 81 * @throws PdfParserException 82 */ 83 public function getPdfVersion() 84 { 85 return \implode('.', $this->parser->getPdfVersion()); 86 } 87 88 /** 89 * Get the page count. 90 * 91 * @return int 92 * @throws PdfTypeException 93 * @throws CrossReferenceException 94 * @throws PdfParserException 95 */ 96 public function getPageCount() 97 { 98 if ($this->pageCount === null) { 99 $catalog = $this->parser->getCatalog(); 100 101 $pages = PdfType::resolve(PdfDictionary::get($catalog, 'Pages'), $this->parser); 102 $count = PdfType::resolve(PdfDictionary::get($pages, 'Count'), $this->parser); 103 104 $this->pageCount = PdfNumeric::ensure($count)->value; 105 } 106 107 return $this->pageCount; 108 } 109 110 /** 111 * Get a page instance. 112 * 113 * @param int $pageNumber 114 * @return Page 115 * @throws PdfTypeException 116 * @throws CrossReferenceException 117 * @throws PdfParserException 118 * @throws \InvalidArgumentException 119 */ 120 public function getPage($pageNumber) 121 { 122 if (!\is_numeric($pageNumber)) { 123 throw new \InvalidArgumentException( 124 'Page number needs to be a number.' 125 ); 126 } 127 128 if ($pageNumber < 1 || $pageNumber > $this->getPageCount()) { 129 throw new \InvalidArgumentException( 130 \sprintf( 131 'Page number "%s" out of available page range (1 - %s)', 132 $pageNumber, 133 $this->getPageCount() 134 ) 135 ); 136 } 137 138 $this->readPages(); 139 140 $page = $this->pages[$pageNumber - 1]; 141 142 if ($page instanceof PdfIndirectObjectReference) { 143 $readPages = function ($kids) use (&$readPages) { 144 $kids = PdfArray::ensure($kids); 145 146 /** @noinspection LoopWhichDoesNotLoopInspection */ 147 foreach ($kids->value as $reference) { 148 $reference = PdfIndirectObjectReference::ensure($reference); 149 $object = $this->parser->getIndirectObject($reference->value); 150 $type = PdfDictionary::get($object->value, 'Type'); 151 152 if ($type->value === 'Pages') { 153 return $readPages(PdfDictionary::get($object->value, 'Kids')); 154 } 155 156 return $object; 157 } 158 159 throw new PdfReaderException( 160 'Kids array cannot be empty.', 161 PdfReaderException::KIDS_EMPTY 162 ); 163 }; 164 165 $page = $this->parser->getIndirectObject($page->value); 166 $dict = PdfType::resolve($page, $this->parser); 167 $type = PdfDictionary::get($dict, 'Type'); 168 if ($type->value === 'Pages') { 169 $kids = PdfType::resolve(PdfDictionary::get($dict, 'Kids'), $this->parser); 170 $page = $this->pages[$pageNumber - 1] = $readPages($kids); 171 } else { 172 $this->pages[$pageNumber - 1] = $page; 173 } 174 } 175 176 return new Page($page, $this->parser); 177 } 178 179 /** 180 * Walk the page tree and resolve all indirect objects of all pages. 181 * 182 * @throws PdfTypeException 183 * @throws CrossReferenceException 184 * @throws PdfParserException 185 */ 186 protected function readPages() 187 { 188 if (\count($this->pages) > 0) { 189 return; 190 } 191 192 $readPages = function ($kids, $count) use (&$readPages) { 193 $kids = PdfArray::ensure($kids); 194 $isLeaf = $count->value === \count($kids->value); 195 196 foreach ($kids->value as $reference) { 197 $reference = PdfIndirectObjectReference::ensure($reference); 198 199 if ($isLeaf) { 200 $this->pages[] = $reference; 201 continue; 202 } 203 204 $object = $this->parser->getIndirectObject($reference->value); 205 $type = PdfDictionary::get($object->value, 'Type'); 206 207 if ($type->value === 'Pages') { 208 $readPages(PdfDictionary::get($object->value, 'Kids'), PdfDictionary::get($object->value, 'Count')); 209 } else { 210 $this->pages[] = $object; 211 } 212 } 213 }; 214 215 $catalog = $this->parser->getCatalog(); 216 $pages = PdfType::resolve(PdfDictionary::get($catalog, 'Pages'), $this->parser); 217 $count = PdfType::resolve(PdfDictionary::get($pages, 'Count'), $this->parser); 218 $kids = PdfType::resolve(PdfDictionary::get($pages, 'Kids'), $this->parser); 219 $readPages($kids, $count); 220 } 221 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body