For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

669 lines
21 KiB

  1. <?php
  2. /**
  3. * PHPExcel_Reader_Excel5_Escher
  4. *
  5. * Copyright (c) 2006 - 2015 PHPExcel
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * @category PHPExcel
  22. * @package PHPExcel_Reader_Excel5
  23. * @copyright Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel)
  24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  25. * @version ##VERSION##, ##DATE##
  26. */
  27. class PHPExcel_Reader_Excel5_Escher
  28. {
  29. const DGGCONTAINER = 0xF000;
  30. const BSTORECONTAINER = 0xF001;
  31. const DGCONTAINER = 0xF002;
  32. const SPGRCONTAINER = 0xF003;
  33. const SPCONTAINER = 0xF004;
  34. const DGG = 0xF006;
  35. const BSE = 0xF007;
  36. const DG = 0xF008;
  37. const SPGR = 0xF009;
  38. const SP = 0xF00A;
  39. const OPT = 0xF00B;
  40. const CLIENTTEXTBOX = 0xF00D;
  41. const CLIENTANCHOR = 0xF010;
  42. const CLIENTDATA = 0xF011;
  43. const BLIPJPEG = 0xF01D;
  44. const BLIPPNG = 0xF01E;
  45. const SPLITMENUCOLORS = 0xF11E;
  46. const TERTIARYOPT = 0xF122;
  47. /**
  48. * Escher stream data (binary)
  49. *
  50. * @var string
  51. */
  52. private $data;
  53. /**
  54. * Size in bytes of the Escher stream data
  55. *
  56. * @var int
  57. */
  58. private $dataSize;
  59. /**
  60. * Current position of stream pointer in Escher stream data
  61. *
  62. * @var int
  63. */
  64. private $pos;
  65. /**
  66. * The object to be returned by the reader. Modified during load.
  67. *
  68. * @var mixed
  69. */
  70. private $object;
  71. /**
  72. * Create a new PHPExcel_Reader_Excel5_Escher instance
  73. *
  74. * @param mixed $object
  75. */
  76. public function __construct($object)
  77. {
  78. $this->object = $object;
  79. }
  80. /**
  81. * Load Escher stream data. May be a partial Escher stream.
  82. *
  83. * @param string $data
  84. */
  85. public function load($data)
  86. {
  87. $this->data = $data;
  88. // total byte size of Excel data (workbook global substream + sheet substreams)
  89. $this->dataSize = strlen($this->data);
  90. $this->pos = 0;
  91. // Parse Escher stream
  92. while ($this->pos < $this->dataSize) {
  93. // offset: 2; size: 2: Record Type
  94. $fbt = PHPExcel_Reader_Excel5::getInt2d($this->data, $this->pos + 2);
  95. switch ($fbt) {
  96. case self::DGGCONTAINER:
  97. $this->readDggContainer();
  98. break;
  99. case self::DGG:
  100. $this->readDgg();
  101. break;
  102. case self::BSTORECONTAINER:
  103. $this->readBstoreContainer();
  104. break;
  105. case self::BSE:
  106. $this->readBSE();
  107. break;
  108. case self::BLIPJPEG:
  109. $this->readBlipJPEG();
  110. break;
  111. case self::BLIPPNG:
  112. $this->readBlipPNG();
  113. break;
  114. case self::OPT:
  115. $this->readOPT();
  116. break;
  117. case self::TERTIARYOPT:
  118. $this->readTertiaryOPT();
  119. break;
  120. case self::SPLITMENUCOLORS:
  121. $this->readSplitMenuColors();
  122. break;
  123. case self::DGCONTAINER:
  124. $this->readDgContainer();
  125. break;
  126. case self::DG:
  127. $this->readDg();
  128. break;
  129. case self::SPGRCONTAINER:
  130. $this->readSpgrContainer();
  131. break;
  132. case self::SPCONTAINER:
  133. $this->readSpContainer();
  134. break;
  135. case self::SPGR:
  136. $this->readSpgr();
  137. break;
  138. case self::SP:
  139. $this->readSp();
  140. break;
  141. case self::CLIENTTEXTBOX:
  142. $this->readClientTextbox();
  143. break;
  144. case self::CLIENTANCHOR:
  145. $this->readClientAnchor();
  146. break;
  147. case self::CLIENTDATA:
  148. $this->readClientData();
  149. break;
  150. default:
  151. $this->readDefault();
  152. break;
  153. }
  154. }
  155. return $this->object;
  156. }
  157. /**
  158. * Read a generic record
  159. */
  160. private function readDefault()
  161. {
  162. // offset 0; size: 2; recVer and recInstance
  163. $verInstance = PHPExcel_Reader_Excel5::getInt2d($this->data, $this->pos);
  164. // offset: 2; size: 2: Record Type
  165. $fbt = PHPExcel_Reader_Excel5::getInt2d($this->data, $this->pos + 2);
  166. // bit: 0-3; mask: 0x000F; recVer
  167. $recVer = (0x000F & $verInstance) >> 0;
  168. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  169. $recordData = substr($this->data, $this->pos + 8, $length);
  170. // move stream pointer to next record
  171. $this->pos += 8 + $length;
  172. }
  173. /**
  174. * Read DggContainer record (Drawing Group Container)
  175. */
  176. private function readDggContainer()
  177. {
  178. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  179. $recordData = substr($this->data, $this->pos + 8, $length);
  180. // move stream pointer to next record
  181. $this->pos += 8 + $length;
  182. // record is a container, read contents
  183. $dggContainer = new PHPExcel_Shared_Escher_DggContainer();
  184. $this->object->setDggContainer($dggContainer);
  185. $reader = new PHPExcel_Reader_Excel5_Escher($dggContainer);
  186. $reader->load($recordData);
  187. }
  188. /**
  189. * Read Dgg record (Drawing Group)
  190. */
  191. private function readDgg()
  192. {
  193. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  194. $recordData = substr($this->data, $this->pos + 8, $length);
  195. // move stream pointer to next record
  196. $this->pos += 8 + $length;
  197. }
  198. /**
  199. * Read BstoreContainer record (Blip Store Container)
  200. */
  201. private function readBstoreContainer()
  202. {
  203. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  204. $recordData = substr($this->data, $this->pos + 8, $length);
  205. // move stream pointer to next record
  206. $this->pos += 8 + $length;
  207. // record is a container, read contents
  208. $bstoreContainer = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer();
  209. $this->object->setBstoreContainer($bstoreContainer);
  210. $reader = new PHPExcel_Reader_Excel5_Escher($bstoreContainer);
  211. $reader->load($recordData);
  212. }
  213. /**
  214. * Read BSE record
  215. */
  216. private function readBSE()
  217. {
  218. // offset: 0; size: 2; recVer and recInstance
  219. // bit: 4-15; mask: 0xFFF0; recInstance
  220. $recInstance = (0xFFF0 & PHPExcel_Reader_Excel5::getInt2d($this->data, $this->pos)) >> 4;
  221. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  222. $recordData = substr($this->data, $this->pos + 8, $length);
  223. // move stream pointer to next record
  224. $this->pos += 8 + $length;
  225. // add BSE to BstoreContainer
  226. $BSE = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE();
  227. $this->object->addBSE($BSE);
  228. $BSE->setBLIPType($recInstance);
  229. // offset: 0; size: 1; btWin32 (MSOBLIPTYPE)
  230. $btWin32 = ord($recordData[0]);
  231. // offset: 1; size: 1; btWin32 (MSOBLIPTYPE)
  232. $btMacOS = ord($recordData[1]);
  233. // offset: 2; size: 16; MD4 digest
  234. $rgbUid = substr($recordData, 2, 16);
  235. // offset: 18; size: 2; tag
  236. $tag = PHPExcel_Reader_Excel5::getInt2d($recordData, 18);
  237. // offset: 20; size: 4; size of BLIP in bytes
  238. $size = PHPExcel_Reader_Excel5::getInt4d($recordData, 20);
  239. // offset: 24; size: 4; number of references to this BLIP
  240. $cRef = PHPExcel_Reader_Excel5::getInt4d($recordData, 24);
  241. // offset: 28; size: 4; MSOFO file offset
  242. $foDelay = PHPExcel_Reader_Excel5::getInt4d($recordData, 28);
  243. // offset: 32; size: 1; unused1
  244. $unused1 = ord($recordData{32});
  245. // offset: 33; size: 1; size of nameData in bytes (including null terminator)
  246. $cbName = ord($recordData{33});
  247. // offset: 34; size: 1; unused2
  248. $unused2 = ord($recordData{34});
  249. // offset: 35; size: 1; unused3
  250. $unused3 = ord($recordData{35});
  251. // offset: 36; size: $cbName; nameData
  252. $nameData = substr($recordData, 36, $cbName);
  253. // offset: 36 + $cbName, size: var; the BLIP data
  254. $blipData = substr($recordData, 36 + $cbName);
  255. // record is a container, read contents
  256. $reader = new PHPExcel_Reader_Excel5_Escher($BSE);
  257. $reader->load($blipData);
  258. }
  259. /**
  260. * Read BlipJPEG record. Holds raw JPEG image data
  261. */
  262. private function readBlipJPEG()
  263. {
  264. // offset: 0; size: 2; recVer and recInstance
  265. // bit: 4-15; mask: 0xFFF0; recInstance
  266. $recInstance = (0xFFF0 & PHPExcel_Reader_Excel5::getInt2d($this->data, $this->pos)) >> 4;
  267. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  268. $recordData = substr($this->data, $this->pos + 8, $length);
  269. // move stream pointer to next record
  270. $this->pos += 8 + $length;
  271. $pos = 0;
  272. // offset: 0; size: 16; rgbUid1 (MD4 digest of)
  273. $rgbUid1 = substr($recordData, 0, 16);
  274. $pos += 16;
  275. // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
  276. if (in_array($recInstance, array(0x046B, 0x06E3))) {
  277. $rgbUid2 = substr($recordData, 16, 16);
  278. $pos += 16;
  279. }
  280. // offset: var; size: 1; tag
  281. $tag = ord($recordData{$pos});
  282. $pos += 1;
  283. // offset: var; size: var; the raw image data
  284. $data = substr($recordData, $pos);
  285. $blip = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip();
  286. $blip->setData($data);
  287. $this->object->setBlip($blip);
  288. }
  289. /**
  290. * Read BlipPNG record. Holds raw PNG image data
  291. */
  292. private function readBlipPNG()
  293. {
  294. // offset: 0; size: 2; recVer and recInstance
  295. // bit: 4-15; mask: 0xFFF0; recInstance
  296. $recInstance = (0xFFF0 & PHPExcel_Reader_Excel5::getInt2d($this->data, $this->pos)) >> 4;
  297. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  298. $recordData = substr($this->data, $this->pos + 8, $length);
  299. // move stream pointer to next record
  300. $this->pos += 8 + $length;
  301. $pos = 0;
  302. // offset: 0; size: 16; rgbUid1 (MD4 digest of)
  303. $rgbUid1 = substr($recordData, 0, 16);
  304. $pos += 16;
  305. // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
  306. if ($recInstance == 0x06E1) {
  307. $rgbUid2 = substr($recordData, 16, 16);
  308. $pos += 16;
  309. }
  310. // offset: var; size: 1; tag
  311. $tag = ord($recordData{$pos});
  312. $pos += 1;
  313. // offset: var; size: var; the raw image data
  314. $data = substr($recordData, $pos);
  315. $blip = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip();
  316. $blip->setData($data);
  317. $this->object->setBlip($blip);
  318. }
  319. /**
  320. * Read OPT record. This record may occur within DggContainer record or SpContainer
  321. */
  322. private function readOPT()
  323. {
  324. // offset: 0; size: 2; recVer and recInstance
  325. // bit: 4-15; mask: 0xFFF0; recInstance
  326. $recInstance = (0xFFF0 & PHPExcel_Reader_Excel5::getInt2d($this->data, $this->pos)) >> 4;
  327. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  328. $recordData = substr($this->data, $this->pos + 8, $length);
  329. // move stream pointer to next record
  330. $this->pos += 8 + $length;
  331. $this->readOfficeArtRGFOPTE($recordData, $recInstance);
  332. }
  333. /**
  334. * Read TertiaryOPT record
  335. */
  336. private function readTertiaryOPT()
  337. {
  338. // offset: 0; size: 2; recVer and recInstance
  339. // bit: 4-15; mask: 0xFFF0; recInstance
  340. $recInstance = (0xFFF0 & PHPExcel_Reader_Excel5::getInt2d($this->data, $this->pos)) >> 4;
  341. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  342. $recordData = substr($this->data, $this->pos + 8, $length);
  343. // move stream pointer to next record
  344. $this->pos += 8 + $length;
  345. }
  346. /**
  347. * Read SplitMenuColors record
  348. */
  349. private function readSplitMenuColors()
  350. {
  351. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  352. $recordData = substr($this->data, $this->pos + 8, $length);
  353. // move stream pointer to next record
  354. $this->pos += 8 + $length;
  355. }
  356. /**
  357. * Read DgContainer record (Drawing Container)
  358. */
  359. private function readDgContainer()
  360. {
  361. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  362. $recordData = substr($this->data, $this->pos + 8, $length);
  363. // move stream pointer to next record
  364. $this->pos += 8 + $length;
  365. // record is a container, read contents
  366. $dgContainer = new PHPExcel_Shared_Escher_DgContainer();
  367. $this->object->setDgContainer($dgContainer);
  368. $reader = new PHPExcel_Reader_Excel5_Escher($dgContainer);
  369. $escher = $reader->load($recordData);
  370. }
  371. /**
  372. * Read Dg record (Drawing)
  373. */
  374. private function readDg()
  375. {
  376. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  377. $recordData = substr($this->data, $this->pos + 8, $length);
  378. // move stream pointer to next record
  379. $this->pos += 8 + $length;
  380. }
  381. /**
  382. * Read SpgrContainer record (Shape Group Container)
  383. */
  384. private function readSpgrContainer()
  385. {
  386. // context is either context DgContainer or SpgrContainer
  387. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  388. $recordData = substr($this->data, $this->pos + 8, $length);
  389. // move stream pointer to next record
  390. $this->pos += 8 + $length;
  391. // record is a container, read contents
  392. $spgrContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer();
  393. if ($this->object instanceof PHPExcel_Shared_Escher_DgContainer) {
  394. // DgContainer
  395. $this->object->setSpgrContainer($spgrContainer);
  396. } else {
  397. // SpgrContainer
  398. $this->object->addChild($spgrContainer);
  399. }
  400. $reader = new PHPExcel_Reader_Excel5_Escher($spgrContainer);
  401. $escher = $reader->load($recordData);
  402. }
  403. /**
  404. * Read SpContainer record (Shape Container)
  405. */
  406. private function readSpContainer()
  407. {
  408. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  409. $recordData = substr($this->data, $this->pos + 8, $length);
  410. // add spContainer to spgrContainer
  411. $spContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer();
  412. $this->object->addChild($spContainer);
  413. // move stream pointer to next record
  414. $this->pos += 8 + $length;
  415. // record is a container, read contents
  416. $reader = new PHPExcel_Reader_Excel5_Escher($spContainer);
  417. $escher = $reader->load($recordData);
  418. }
  419. /**
  420. * Read Spgr record (Shape Group)
  421. */
  422. private function readSpgr()
  423. {
  424. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  425. $recordData = substr($this->data, $this->pos + 8, $length);
  426. // move stream pointer to next record
  427. $this->pos += 8 + $length;
  428. }
  429. /**
  430. * Read Sp record (Shape)
  431. */
  432. private function readSp()
  433. {
  434. // offset: 0; size: 2; recVer and recInstance
  435. // bit: 4-15; mask: 0xFFF0; recInstance
  436. $recInstance = (0xFFF0 & PHPExcel_Reader_Excel5::getInt2d($this->data, $this->pos)) >> 4;
  437. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  438. $recordData = substr($this->data, $this->pos + 8, $length);
  439. // move stream pointer to next record
  440. $this->pos += 8 + $length;
  441. }
  442. /**
  443. * Read ClientTextbox record
  444. */
  445. private function readClientTextbox()
  446. {
  447. // offset: 0; size: 2; recVer and recInstance
  448. // bit: 4-15; mask: 0xFFF0; recInstance
  449. $recInstance = (0xFFF0 & PHPExcel_Reader_Excel5::getInt2d($this->data, $this->pos)) >> 4;
  450. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  451. $recordData = substr($this->data, $this->pos + 8, $length);
  452. // move stream pointer to next record
  453. $this->pos += 8 + $length;
  454. }
  455. /**
  456. * Read ClientAnchor record. This record holds information about where the shape is anchored in worksheet
  457. */
  458. private function readClientAnchor()
  459. {
  460. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  461. $recordData = substr($this->data, $this->pos + 8, $length);
  462. // move stream pointer to next record
  463. $this->pos += 8 + $length;
  464. // offset: 2; size: 2; upper-left corner column index (0-based)
  465. $c1 = PHPExcel_Reader_Excel5::getInt2d($recordData, 2);
  466. // offset: 4; size: 2; upper-left corner horizontal offset in 1/1024 of column width
  467. $startOffsetX = PHPExcel_Reader_Excel5::getInt2d($recordData, 4);
  468. // offset: 6; size: 2; upper-left corner row index (0-based)
  469. $r1 = PHPExcel_Reader_Excel5::getInt2d($recordData, 6);
  470. // offset: 8; size: 2; upper-left corner vertical offset in 1/256 of row height
  471. $startOffsetY = PHPExcel_Reader_Excel5::getInt2d($recordData, 8);
  472. // offset: 10; size: 2; bottom-right corner column index (0-based)
  473. $c2 = PHPExcel_Reader_Excel5::getInt2d($recordData, 10);
  474. // offset: 12; size: 2; bottom-right corner horizontal offset in 1/1024 of column width
  475. $endOffsetX = PHPExcel_Reader_Excel5::getInt2d($recordData, 12);
  476. // offset: 14; size: 2; bottom-right corner row index (0-based)
  477. $r2 = PHPExcel_Reader_Excel5::getInt2d($recordData, 14);
  478. // offset: 16; size: 2; bottom-right corner vertical offset in 1/256 of row height
  479. $endOffsetY = PHPExcel_Reader_Excel5::getInt2d($recordData, 16);
  480. // set the start coordinates
  481. $this->object->setStartCoordinates(PHPExcel_Cell::stringFromColumnIndex($c1) . ($r1 + 1));
  482. // set the start offsetX
  483. $this->object->setStartOffsetX($startOffsetX);
  484. // set the start offsetY
  485. $this->object->setStartOffsetY($startOffsetY);
  486. // set the end coordinates
  487. $this->object->setEndCoordinates(PHPExcel_Cell::stringFromColumnIndex($c2) . ($r2 + 1));
  488. // set the end offsetX
  489. $this->object->setEndOffsetX($endOffsetX);
  490. // set the end offsetY
  491. $this->object->setEndOffsetY($endOffsetY);
  492. }
  493. /**
  494. * Read ClientData record
  495. */
  496. private function readClientData()
  497. {
  498. $length = PHPExcel_Reader_Excel5::getInt4d($this->data, $this->pos + 4);
  499. $recordData = substr($this->data, $this->pos + 8, $length);
  500. // move stream pointer to next record
  501. $this->pos += 8 + $length;
  502. }
  503. /**
  504. * Read OfficeArtRGFOPTE table of property-value pairs
  505. *
  506. * @param string $data Binary data
  507. * @param int $n Number of properties
  508. */
  509. private function readOfficeArtRGFOPTE($data, $n)
  510. {
  511. $splicedComplexData = substr($data, 6 * $n);
  512. // loop through property-value pairs
  513. for ($i = 0; $i < $n; ++$i) {
  514. // read 6 bytes at a time
  515. $fopte = substr($data, 6 * $i, 6);
  516. // offset: 0; size: 2; opid
  517. $opid = PHPExcel_Reader_Excel5::getInt2d($fopte, 0);
  518. // bit: 0-13; mask: 0x3FFF; opid.opid
  519. $opidOpid = (0x3FFF & $opid) >> 0;
  520. // bit: 14; mask 0x4000; 1 = value in op field is BLIP identifier
  521. $opidFBid = (0x4000 & $opid) >> 14;
  522. // bit: 15; mask 0x8000; 1 = this is a complex property, op field specifies size of complex data
  523. $opidFComplex = (0x8000 & $opid) >> 15;
  524. // offset: 2; size: 4; the value for this property
  525. $op = PHPExcel_Reader_Excel5::getInt4d($fopte, 2);
  526. if ($opidFComplex) {
  527. $complexData = substr($splicedComplexData, 0, $op);
  528. $splicedComplexData = substr($splicedComplexData, $op);
  529. // we store string value with complex data
  530. $value = $complexData;
  531. } else {
  532. // we store integer value
  533. $value = $op;
  534. }
  535. $this->object->setOPT($opidOpid, $value);
  536. }
  537. }
  538. }