PageRenderTime 58ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/library/php/odtphp/odf.php

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