No Description

Image.php 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. <?php
  2. /**
  3. * This file is part of PHPWord - A pure PHP library for reading and writing
  4. * word processing documents.
  5. *
  6. * PHPWord is free software distributed under the terms of the GNU Lesser
  7. * General Public License version 3 as published by the Free Software Foundation.
  8. *
  9. * For the full copyright and license information, please read the LICENSE
  10. * file that was distributed with this source code. For the full list of
  11. * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
  12. *
  13. * @link https://github.com/PHPOffice/PHPWord
  14. * @copyright 2010-2014 PHPWord contributors
  15. * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
  16. */
  17. namespace PhpOffice\PhpWord\Element;
  18. use PhpOffice\PhpWord\Exception\CreateTemporaryFileException;
  19. use PhpOffice\PhpWord\Exception\InvalidImageException;
  20. use PhpOffice\PhpWord\Exception\UnsupportedImageTypeException;
  21. use PhpOffice\PhpWord\Settings;
  22. use PhpOffice\PhpWord\Shared\ZipArchive;
  23. use PhpOffice\PhpWord\Style\Image as ImageStyle;
  24. /**
  25. * Image element
  26. */
  27. class Image extends AbstractElement
  28. {
  29. /**
  30. * Image source type constants
  31. */
  32. const SOURCE_LOCAL = 'local'; // Local images
  33. const SOURCE_GD = 'gd'; // Generated using GD
  34. const SOURCE_ARCHIVE = 'archive'; // Image in archives zip://$archive#$image
  35. /**
  36. * Image source
  37. *
  38. * @var string
  39. */
  40. private $source;
  41. /**
  42. * Source type: local|gd|archive
  43. *
  44. * @var string
  45. */
  46. private $sourceType;
  47. /**
  48. * Image style
  49. *
  50. * @var ImageStyle
  51. */
  52. private $style;
  53. /**
  54. * Is watermark
  55. *
  56. * @var boolean
  57. */
  58. private $watermark;
  59. /**
  60. * Image type
  61. *
  62. * @var string
  63. */
  64. private $imageType;
  65. /**
  66. * Image create function
  67. *
  68. * @var string
  69. */
  70. private $imageCreateFunc;
  71. /**
  72. * Image function
  73. *
  74. * @var string
  75. */
  76. private $imageFunc;
  77. /**
  78. * Image extension
  79. *
  80. * @var string
  81. */
  82. private $imageExtension;
  83. /**
  84. * Is memory image
  85. *
  86. * @var boolean
  87. */
  88. private $memoryImage;
  89. /**
  90. * Image target file name
  91. *
  92. * @var string
  93. */
  94. private $target;
  95. /**
  96. * Image media index
  97. *
  98. * @var integer
  99. */
  100. private $mediaIndex;
  101. /**
  102. * Has media relation flag; true for Link, Image, and Object
  103. *
  104. * @var bool
  105. */
  106. protected $mediaRelation = true;
  107. /**
  108. * Create new image element
  109. *
  110. * @param string $source
  111. * @param mixed $style
  112. * @param boolean $watermark
  113. * @throws \PhpOffice\PhpWord\Exception\InvalidImageException
  114. * @throws \PhpOffice\PhpWord\Exception\UnsupportedImageTypeException
  115. */
  116. public function __construct($source, $style = null, $watermark = false)
  117. {
  118. $this->source = $source;
  119. $this->setIsWatermark($watermark);
  120. $this->style = $this->setNewStyle(new ImageStyle(), $style, true);
  121. $this->checkImage($source);
  122. }
  123. /**
  124. * Get Image style
  125. *
  126. * @return ImageStyle
  127. */
  128. public function getStyle()
  129. {
  130. return $this->style;
  131. }
  132. /**
  133. * Get image source
  134. *
  135. * @return string
  136. */
  137. public function getSource()
  138. {
  139. return $this->source;
  140. }
  141. /**
  142. * Get image source type
  143. *
  144. * @return string
  145. */
  146. public function getSourceType()
  147. {
  148. return $this->sourceType;
  149. }
  150. /**
  151. * Get image media ID
  152. *
  153. * @return string
  154. */
  155. public function getMediaId()
  156. {
  157. return md5($this->source);
  158. }
  159. /**
  160. * Get is watermark
  161. *
  162. * @return boolean
  163. */
  164. public function isWatermark()
  165. {
  166. return $this->watermark;
  167. }
  168. /**
  169. * Set is watermark
  170. *
  171. * @param boolean $value
  172. */
  173. public function setIsWatermark($value)
  174. {
  175. $this->watermark = $value;
  176. }
  177. /**
  178. * Get image type
  179. *
  180. * @return string
  181. */
  182. public function getImageType()
  183. {
  184. return $this->imageType;
  185. }
  186. /**
  187. * Get image create function
  188. *
  189. * @return string
  190. */
  191. public function getImageCreateFunction()
  192. {
  193. return $this->imageCreateFunc;
  194. }
  195. /**
  196. * Get image function
  197. *
  198. * @return string
  199. */
  200. public function getImageFunction()
  201. {
  202. return $this->imageFunc;
  203. }
  204. /**
  205. * Get image extension
  206. *
  207. * @return string
  208. */
  209. public function getImageExtension()
  210. {
  211. return $this->imageExtension;
  212. }
  213. /**
  214. * Get is memory image
  215. *
  216. * @return boolean
  217. */
  218. public function isMemImage()
  219. {
  220. return $this->memoryImage;
  221. }
  222. /**
  223. * Get target file name
  224. *
  225. * @return string
  226. */
  227. public function getTarget()
  228. {
  229. return $this->target;
  230. }
  231. /**
  232. * Set target file name.
  233. *
  234. * @param string $value
  235. * @return void
  236. */
  237. public function setTarget($value)
  238. {
  239. $this->target = $value;
  240. }
  241. /**
  242. * Get media index
  243. *
  244. * @return integer
  245. */
  246. public function getMediaIndex()
  247. {
  248. return $this->mediaIndex;
  249. }
  250. /**
  251. * Set media index.
  252. *
  253. * @param integer $value
  254. * @return void
  255. */
  256. public function setMediaIndex($value)
  257. {
  258. $this->mediaIndex = $value;
  259. }
  260. /**
  261. * Get image string data
  262. *
  263. * @param bool $base64
  264. * @return string|null
  265. * @since 0.11.0
  266. */
  267. public function getImageStringData($base64 = false)
  268. {
  269. $source = $this->source;
  270. $actualSource = null;
  271. $imageBinary = null;
  272. $imageData = null;
  273. $isTemp = false;
  274. // Get actual source from archive image or other source
  275. // Return null if not found
  276. if ($this->sourceType == self::SOURCE_ARCHIVE) {
  277. $source = substr($source, 6);
  278. list($zipFilename, $imageFilename) = explode('#', $source);
  279. $zip = new ZipArchive();
  280. if ($zip->open($zipFilename) !== false) {
  281. if ($zip->locateName($imageFilename)) {
  282. $isTemp = true;
  283. $zip->extractTo(Settings::getTempDir(), $imageFilename);
  284. $actualSource = Settings::getTempDir() . DIRECTORY_SEPARATOR . $imageFilename;
  285. }
  286. }
  287. $zip->close();
  288. } else {
  289. $actualSource = $source;
  290. }
  291. // Can't find any case where $actualSource = null hasn't captured by
  292. // preceding exceptions. Please uncomment when you find the case and
  293. // put the case into Element\ImageTest.
  294. // if ($actualSource === null) {
  295. // return null;
  296. // }
  297. // Read image binary data and convert to hex/base64 string
  298. if ($this->sourceType == self::SOURCE_GD) {
  299. $imageResource = call_user_func($this->imageCreateFunc, $actualSource);
  300. ob_start();
  301. call_user_func($this->imageFunc, $imageResource);
  302. $imageBinary = ob_get_contents();
  303. ob_end_clean();
  304. } else {
  305. $fileHandle = fopen($actualSource, 'rb', false);
  306. if ($fileHandle !== false) {
  307. $imageBinary = fread($fileHandle, filesize($actualSource));
  308. fclose($fileHandle);
  309. }
  310. }
  311. if ($imageBinary !== null) {
  312. if ($base64) {
  313. $imageData = chunk_split(base64_encode($imageBinary));
  314. } else {
  315. $imageData = chunk_split(bin2hex($imageBinary));
  316. }
  317. }
  318. // Delete temporary file if necessary
  319. if ($isTemp === true) {
  320. @unlink($actualSource);
  321. }
  322. return $imageData;
  323. }
  324. /**
  325. * Check memory image, supported type, image functions, and proportional width/height.
  326. *
  327. * @param string $source
  328. * @return void
  329. * @throws \PhpOffice\PhpWord\Exception\InvalidImageException
  330. * @throws \PhpOffice\PhpWord\Exception\UnsupportedImageTypeException
  331. */
  332. private function checkImage($source)
  333. {
  334. $this->setSourceType($source);
  335. // Check image data
  336. if ($this->sourceType == self::SOURCE_ARCHIVE) {
  337. $imageData = $this->getArchiveImageSize($source);
  338. } else {
  339. $imageData = @getimagesize($source);
  340. }
  341. if (!is_array($imageData)) {
  342. throw new InvalidImageException();
  343. }
  344. list($actualWidth, $actualHeight, $imageType) = $imageData;
  345. // Check image type support
  346. $supportedTypes = array(IMAGETYPE_JPEG, IMAGETYPE_GIF, IMAGETYPE_PNG);
  347. if ($this->sourceType != self::SOURCE_GD) {
  348. $supportedTypes = array_merge($supportedTypes, array(IMAGETYPE_BMP, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM));
  349. }
  350. if (!in_array($imageType, $supportedTypes)) {
  351. throw new UnsupportedImageTypeException();
  352. }
  353. // Define image functions
  354. $this->imageType = image_type_to_mime_type($imageType);
  355. $this->setFunctions();
  356. $this->setProportionalSize($actualWidth, $actualHeight);
  357. }
  358. /**
  359. * Set source type.
  360. *
  361. * @param string $source
  362. * @return void
  363. */
  364. private function setSourceType($source)
  365. {
  366. if (stripos(strrev($source), strrev('.php')) === 0) {
  367. $this->memoryImage = true;
  368. $this->sourceType = self::SOURCE_GD;
  369. } elseif (strpos($source, 'zip://') !== false) {
  370. $this->memoryImage = false;
  371. $this->sourceType = self::SOURCE_ARCHIVE;
  372. } else {
  373. $this->memoryImage = (filter_var($source, FILTER_VALIDATE_URL) !== false);
  374. $this->sourceType = $this->memoryImage ? self::SOURCE_GD : self::SOURCE_LOCAL;
  375. }
  376. }
  377. /**
  378. * Get image size from archive
  379. *
  380. * @since 0.12.0 Throws CreateTemporaryFileException.
  381. *
  382. * @param string $source
  383. * @return array|null
  384. * @throws \PhpOffice\PhpWord\Exception\CreateTemporaryFileException
  385. */
  386. private function getArchiveImageSize($source)
  387. {
  388. $imageData = null;
  389. $source = substr($source, 6);
  390. list($zipFilename, $imageFilename) = explode('#', $source);
  391. $tempFilename = tempnam(Settings::getTempDir(), 'PHPWordImage');
  392. if (false === $tempFilename) {
  393. throw new CreateTemporaryFileException();
  394. }
  395. $zip = new ZipArchive();
  396. if ($zip->open($zipFilename) !== false) {
  397. if ($zip->locateName($imageFilename)) {
  398. $imageContent = $zip->getFromName($imageFilename);
  399. if ($imageContent !== false) {
  400. file_put_contents($tempFilename, $imageContent);
  401. $imageData = getimagesize($tempFilename);
  402. unlink($tempFilename);
  403. }
  404. }
  405. $zip->close();
  406. }
  407. return $imageData;
  408. }
  409. /**
  410. * Set image functions and extensions.
  411. *
  412. * @return void
  413. */
  414. private function setFunctions()
  415. {
  416. switch ($this->imageType) {
  417. case 'image/png':
  418. $this->imageCreateFunc = 'imagecreatefrompng';
  419. $this->imageFunc = 'imagepng';
  420. $this->imageExtension = 'png';
  421. break;
  422. case 'image/gif':
  423. $this->imageCreateFunc = 'imagecreatefromgif';
  424. $this->imageFunc = 'imagegif';
  425. $this->imageExtension = 'gif';
  426. break;
  427. case 'image/jpeg':
  428. case 'image/jpg':
  429. $this->imageCreateFunc = 'imagecreatefromjpeg';
  430. $this->imageFunc = 'imagejpeg';
  431. $this->imageExtension = 'jpg';
  432. break;
  433. case 'image/bmp':
  434. case 'image/x-ms-bmp':
  435. $this->imageType = 'image/bmp';
  436. $this->imageExtension = 'bmp';
  437. break;
  438. case 'image/tiff':
  439. $this->imageExtension = 'tif';
  440. break;
  441. }
  442. }
  443. /**
  444. * Set proportional width/height if one dimension not available.
  445. *
  446. * @param integer $actualWidth
  447. * @param integer $actualHeight
  448. * @return void
  449. */
  450. private function setProportionalSize($actualWidth, $actualHeight)
  451. {
  452. $styleWidth = $this->style->getWidth();
  453. $styleHeight = $this->style->getHeight();
  454. if (!($styleWidth && $styleHeight)) {
  455. if ($styleWidth == null && $styleHeight == null) {
  456. $this->style->setWidth($actualWidth);
  457. $this->style->setHeight($actualHeight);
  458. } elseif ($styleWidth) {
  459. $this->style->setHeight($actualHeight * ($styleWidth / $actualWidth));
  460. } else {
  461. $this->style->setWidth($actualWidth * ($styleHeight / $actualHeight));
  462. }
  463. }
  464. }
  465. /**
  466. * Get is watermark
  467. *
  468. * @deprecated 0.10.0
  469. * @codeCoverageIgnore
  470. */
  471. public function getIsWatermark()
  472. {
  473. return $this->isWatermark();
  474. }
  475. /**
  476. * Get is memory image
  477. *
  478. * @deprecated 0.10.0
  479. * @codeCoverageIgnore
  480. */
  481. public function getIsMemImage()
  482. {
  483. return $this->isMemImage();
  484. }
  485. }