PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/common/libraries/plugin/odtPHP/library/odf.php

https://bitbucket.org/chamilo/chamilo/
PHP | 369 lines | 226 code | 15 blank | 128 comment | 32 complexity | 83cd209d133729ff6af1c14d4ad506b5 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1, LGPL-3.0, GPL-3.0, MIT
  1. <?php
  2. require 'zip/PclZipProxy.php';
  3. require 'zip/PhpZipProxy.php';
  4. require 'Segment.php';
  5. class OdfException extends Exception
  6. {}
  7. /**
  8. * Templating class for odt file
  9. * You need PHP 5.2 at least
  10. * You need Zip Extension or PclZip library
  11. * Encoding : ISO-8859-1
  12. * Last commit by $Author: neveldo $
  13. * Date - $Date: 2009-06-17 11:11:57 +0200 (mer., 17 juin 2009) $
  14. * SVN Revision - $Rev: 42 $
  15. * Id : $Id: odf.php 42 2009-06-17 09:11:57Z neveldo $
  16. *
  17. * @copyright GPL License 2008 - Julien Pauli - Cyril PIERRE de GEYER - Anaska (http://www.anaska.com)
  18. * @license http://www.gnu.org/copyleft/gpl.html GPL License
  19. * @version 1.3
  20. */
  21. class Odf
  22. {
  23. protected $config = array(
  24. 'ZIP_PROXY' => 'PclZipProxy',
  25. 'DELIMITER_LEFT' => '{',
  26. 'DELIMITER_RIGHT' => '}'
  27. );
  28. protected $file;
  29. protected $contentXml;
  30. protected $tmpfile;
  31. protected $manifestXml;
  32. protected $images = array();
  33. protected $vars = array();
  34. protected $segments = array();
  35. const PIXEL_TO_CM = 0.026458333;
  36. /**
  37. * Class constructor
  38. *
  39. * @param string $filename the name of the odt file
  40. * @throws OdfException
  41. */
  42. public function __construct($filename, $config = array())
  43. {
  44. if (! is_array($config)) {
  45. throw new OdfException('Configuration data must be provided as array');
  46. }
  47. foreach ($config as $configKey => $configValue) {
  48. if (array_key_exists($configKey, $this->config)) {
  49. $this->config[$configKey] = $configValue;
  50. }
  51. }
  52. if (! class_exists($this->config['ZIP_PROXY'])) {
  53. throw new OdfException($this->config['ZIP_PROXY'] . ' class not found - check your php settings');
  54. }
  55. $zipHandler = $this->config['ZIP_PROXY'];
  56. $this->file = new $zipHandler();
  57. if ($this->file->open($filename) !== true) {
  58. throw new OdfException("Error while Opening the file '$filename' - Check your odt file");
  59. }
  60. if (($this->contentXml = $this->file->getFromName('content.xml')) === false) {
  61. throw new OdfException("Nothing to parse - check that the content.xml file is correctly formed");
  62. }
  63. if (($this->manifestXml = $this->file->getFromName('META-INF/manifest.xml')) === false) {
  64. throw new OdfException("Something is wrong with META-INF/manifest.xml");
  65. }
  66. $this->file->close();
  67. $tmp = tempnam($this->config['PATH_TO_TMP'], md5(uniqid()));
  68. copy($filename, $tmp);
  69. $this->tmpfile = $tmp;
  70. $this->_moveRowSegments();
  71. }
  72. /**
  73. * Assing a template variable
  74. *
  75. * @param string $key name of the variable within the template
  76. * @param string $value replacement value
  77. * @param bool $encode if true, special XML characters are encoded
  78. * @throws OdfException
  79. * @return odf
  80. */
  81. public function setVars($key, $value, $encode = true, $charset = 'ISO-8859')
  82. {
  83. if (strpos($this->contentXml, $this->config['DELIMITER_LEFT'] . $key . $this->config['DELIMITER_RIGHT']) === false) {
  84. throw new OdfException("var $key not found in the document");
  85. }
  86. $value = $encode ? htmlspecialchars($value) : $value;
  87. $value = ($charset == 'ISO-8859') ? utf8_encode($value) : $value;
  88. $this->vars[$this->config['DELIMITER_LEFT'] . $key . $this->config['DELIMITER_RIGHT']] = str_replace("\n", "<text:line-break/>", $value);
  89. return $this;
  90. }
  91. /**
  92. * Assign a template variable as a picture
  93. *
  94. * @param string $key name of the variable within the template
  95. * @param string $value path to the picture
  96. * @throws OdfException
  97. * @return odf
  98. */
  99. public function setImage($key, $value)
  100. {
  101. $filename = strtok(strrchr($value, '/'), '/.');
  102. $file = substr(strrchr($value, '/'), 1);
  103. $size = @getimagesize($value);
  104. if ($size === false) {
  105. throw new OdfException("Invalid image");
  106. }
  107. list ($width, $height) = $size;
  108. $width *= self::PIXEL_TO_CM;
  109. $height *= self::PIXEL_TO_CM;
  110. $xml = <<<IMG
  111. <draw:frame draw:style-name="fr1" draw:name="$filename" text:anchor-type="as-char" svg:width="{$width}cm" svg:height="{$height}cm" draw:z-index="3"><draw:image xlink:href="Pictures/$file" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/></draw:frame>
  112. IMG;
  113. $this->images[$value] = $file;
  114. $this->setVars($key, $xml, false);
  115. return $this;
  116. }
  117. public function setImageReplace($key, $value)
  118. {
  119. $filename = strtok(strrchr($value, '/'), '/.');
  120. $file = substr(strrchr($value, '/'), 1);
  121. $size = @getimagesize($value);
  122. if ($size === false) {
  123. throw new OdfException("Invalid image");
  124. }
  125. $this->images[$value] = $file;
  126. $this->vars[$key] = $file;
  127. return $this;
  128. }
  129. /**
  130. * Move segment tags for lines of tables
  131. * Called automatically within the constructor
  132. *
  133. * @return void
  134. */
  135. private function _moveRowSegments()
  136. {
  137. // Search all possible rows in the document
  138. $reg1 = "#<table:table-row[^>]*>(.*)</table:table-row>#smU";
  139. preg_match_all($reg1, $this->contentXml, $matches);
  140. for ($i = 0, $size = count($matches[0]); $i < $size; $i++) {
  141. // Check if the current row contains a segment row.*
  142. $reg2 = '#\[!--\sBEGIN\s(row.[\S]*)\s--\](.*)\[!--\sEND\s\\1\s--\]#sm';
  143. if (preg_match($reg2, $matches[0][$i], $matches2)) {
  144. $balise = str_replace('row.', '', $matches2[1]);
  145. // Move segment tags around the row
  146. $replace = array(
  147. '[!-- BEGIN ' . $matches2[1] . ' --]' => '',
  148. '[!-- END ' . $matches2[1] . ' --]' => '',
  149. '<table:table-row' => '[!-- BEGIN ' . $balise . ' --]<table:table-row',
  150. '</table:table-row>' => '</table:table-row>[!-- END ' . $balise . ' --]'
  151. );
  152. $replacedXML = str_replace(array_keys($replace), array_values($replace), $matches[0][$i]);
  153. $this->contentXml = str_replace($matches[0][$i], $replacedXML, $this->contentXml);
  154. }
  155. }
  156. }
  157. /**
  158. * Merge template variables
  159. * Called automatically for a save
  160. *
  161. * @return void
  162. */
  163. private function _parse()
  164. {
  165. $this->contentXml = str_replace(array_keys($this->vars), array_values($this->vars), $this->contentXml);
  166. }
  167. /**
  168. * Add the merged segment to the document
  169. *
  170. * @param Segment $segment
  171. * @throws OdfException
  172. * @return odf
  173. */
  174. public function mergeSegment(Segment $segment)
  175. {
  176. if (! array_key_exists($segment->getName(), $this->segments)) {
  177. throw new OdfException($segment->getName() . 'cannot be parsed, has it been set yet ?');
  178. }
  179. $string = $segment->getName();
  180. // $reg = '@<text:p[^>]*>\[!--\sBEGIN\s' . $string . '\s--\](.*)\[!--.+END\s' . $string . '\s--\]<\/text:p>@smU';
  181. $reg = '@\[!--\sBEGIN\s' . $string . '\s--\](.*)\[!--.+END\s' . $string . '\s--\]@smU';
  182. $this->contentXml = preg_replace($reg, $segment->getXmlParsed(), $this->contentXml);
  183. return $this;
  184. }
  185. /**
  186. * Display all the current template variables
  187. *
  188. * @return string
  189. */
  190. public function printVars()
  191. {
  192. return print_r('<pre>' . print_r($this->vars, true) . '</pre>', true);
  193. }
  194. /**
  195. * Display the XML content of the file from odt document
  196. * as it is at the moment
  197. *
  198. * @return string
  199. */
  200. public function __toString()
  201. {
  202. return $this->contentXml;
  203. }
  204. /**
  205. * Display loop segments declared with setSegment()
  206. *
  207. * @return string
  208. */
  209. public function printDeclaredSegments()
  210. {
  211. return '<pre>' . print_r(implode(' ', array_keys($this->segments)), true) . '</pre>';
  212. }
  213. /**
  214. * Declare a segment in order to use it in a loop
  215. *
  216. * @param string $segment
  217. * @throws OdfException
  218. * @return Segment
  219. */
  220. public function setSegment($segment) {
  221. if (array_key_exists($segment, $this->segments)) {
  222. return $this->segments[$segment];
  223. }
  224. $reg = "#\[!--\sBEGIN\s$segment\s--\](.*)\[!--\sEND\s$segment\s--\]#sm";
  225. for ($counter = 50000; $counter <= 50100; $counter += 1) {
  226. $ss = $this->splitByLength($this->contentXml, $counter);
  227. foreach ($ss as $s) {
  228. if (preg_match($reg, html_entity_decode($s), $m)) {
  229. $this->segments[$segment] = new Segment($segment, $m[1], $this);
  230. return $this->segments[$segment];
  231. }
  232. }
  233. }
  234. throw new OdfException("'$segment' segment not found in the document");
  235. }
  236. function splitByLength($string, $chunkLength=1)
  237. {
  238. $Result = array();
  239. $Remainder = strlen($string) % $chunkLength;
  240. $cycles = ((strlen($string) - $Remainder) / $chunkLength) + (($Remainder != 0) ? 1 : 0);
  241. for ($x=0; $x < $cycles; $x++)
  242. $Result[$x] = substr($string, ($x * $chunkLength), $chunkLength);
  243. return $Result;
  244. }
  245. /**
  246. * Save the odt file on the disk
  247. *
  248. * @param string $file name of the desired file
  249. * @throws OdfException
  250. * @return void
  251. */
  252. public function saveToDisk($file = null)
  253. {
  254. if ($file !== null && is_string($file)) {
  255. if (file_exists($file) && !(is_file($file) && is_writable($file))) {
  256. throw new OdfException('Permission denied : can\'t create ' . $file);
  257. }
  258. $this->_save();
  259. copy($this->tmpfile, $file);
  260. } else {
  261. $this->_save();
  262. }
  263. }
  264. /**
  265. * Internal save
  266. *
  267. * @throws OdfException
  268. * @return void
  269. */
  270. // private function _save()
  271. // {
  272. // $this->file->open($this->tmpfile);
  273. // $this->_parse();
  274. // if (! $this->file->addFromString('content.xml', $this->contentXml)) {
  275. // throw new OdfException('Error during file export');
  276. // }
  277. // foreach ($this->images as $imageKey => $imageValue) {
  278. // $this->file->addFile($imageKey, 'Pictures/' . $imageValue);
  279. // }
  280. // $this->file->close(); // seems to bug on windows CLI sometimes
  281. // }
  282. private function _save()
  283. {
  284. $this->file->open($this->tmpfile);
  285. $this->_parse();
  286. if (! $this->file->addFromString('content.xml', $this->contentXml)) {
  287. throw new OdfException('Error during file export');
  288. }
  289. foreach ($this->images as $imageKey => $imageValue) {
  290. $this->file->addFile($imageKey, 'Pictures/' . $imageValue);
  291. $this->addImageToManifest($imageValue);
  292. }
  293. if (! $this->file->addFromString('META-INF/manifest.xml', $this->manifestXml)) {
  294. throw new OdfException('Error during file export: manifest.xml');
  295. }
  296. $this->file->close(); // seems to bug on windows CLI sometimes
  297. }
  298. public function addImageToManifest($file) {
  299. $extension = explode('.', $file);
  300. $replace = '<manifest:file-entry manifest:media-type="image/'.$extension[1].'" manifest:full-path="Pictures/'.$file.'"/></manifest:manifest>';
  301. $this->manifestXml = str_replace('</manifest:manifest>', $replace, $this->manifestXml);
  302. }
  303. /**
  304. * Export the file as attached file by HTTP
  305. *
  306. * @param string $name (optionnal)
  307. * @throws OdfException
  308. * @return void
  309. */
  310. public function exportAsAttachedFile($name="")
  311. {
  312. $this->_save();
  313. if (headers_sent($filename, $linenum)) {
  314. throw new OdfException("headers already sent ($filename at $linenum)");
  315. }
  316. if( $name == "" )
  317. {
  318. $name = md5(uniqid()) . ".odt";
  319. }
  320. header('Content-type: application/vnd.oasis.opendocument.text');
  321. header('Content-Disposition: attachment; filename="'.$name.'"');
  322. readfile($this->tmpfile);
  323. }
  324. /**
  325. * Returns a variable of configuration
  326. *
  327. * @return string The requested variable of configuration
  328. */
  329. public function getConfig($configKey)
  330. {
  331. if (array_key_exists($configKey, $this->config)) {
  332. return $this->config[$configKey];
  333. }
  334. return false;
  335. }
  336. /**
  337. * Returns the temporary working file
  338. *
  339. * @return string le chemin vers le fichier temporaire de travail
  340. */
  341. public function getTmpfile()
  342. {
  343. return $this->tmpfile;
  344. }
  345. /**
  346. * Delete the temporary file when the object is destroyed
  347. */
  348. public function __destruct() {
  349. if (file_exists($this->tmpfile)) {
  350. unlink($this->tmpfile);
  351. }
  352. }
  353. }
  354. ?>