Escher.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  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. }