3 * our moto... "To Convert and Serve"!!!
6 * $x= new File_Convert("filename", "application/pdf");
7 * $fn = $x->convert("image/jpeg", 200, 0);
11 * $x= new File_Convert("filename", "application/pdf");
12 * $fn = $x->convert("application/pdf"); // does no conversion (as it's the same.
13 * $x->serve('inline'); // can fix IE Mess...
17 * delete_all : delete all the generated files after script execution when we call convert()
24 $x = new File_Convert(false);
25 print_r($x->getConvMethods('application/msword', 'application/pdf'));
26 print_r($x->getConvMethods('application/msword', 'image/jpeg'));
27 print_r($x->getConvMethods('application/acad', 'image/jpeg'));
28 var_dump($x->getConvMethods('application/acad', 'application/msword')); // impossible
30 $x = new File_Convert(file, 'app../excel',array('sheet'=>array(0,1,2...) ));
31 $out = $x->convert('text/csv');
38 static $options = array();
40 var $fn = ''; // filename
43 var $debug = false; // set to true to turn on deubgging
46 var $lastaction = false;
48 var $solutions = array();
50 function __construct($fn, $mimetype, $options=array())
56 if (!file_exists($fn)) {
57 throw new Exception("Source file does not exist:". $fn );
60 $this->mimetype = $mimetype;
61 self::$options = $options;
62 if (isset($options['debug'])) {
63 $this->debug = $options['debug'];
70 * check if conversion exists, and return the filename if it does.
74 function convertExists($toMimetype, $x= 0, $y =0)
76 static $actions = array();
78 if ($toMimetype != $this->mimetype) {
80 if (!isset($actions["{$this->mimetype} => $toMimetype"])) {
81 $actions["{$this->mimetype} => $toMimetype"] = $this->getConvMethods($this->mimetype, $toMimetype);;
84 $action = $actions["{$this->mimetype} => $toMimetype"];
87 // echo '<PRE>';print_r($action);
91 if (!file_exists($this->fn)) {
95 $fn = $action->convertExists($this->fn, $x, $y);
103 if (!preg_match('#^image/#', $toMimetype) || ( empty($x) && empty($y))) {
107 //echo "testing scale image";
108 require_once 'File/Convert/Solution/scaleimage.php';
109 $sc = new File_Convert_Solution_scaleimage($toMimetype, $toMimetype);
110 //$sc->convert = $this;
111 $sc->debug= $this->debug;
112 $this->solutions[] = $sc;
114 if (strpos($x, 'x')) {
115 $bits = explode('x', $x);
117 $y = empty($bits[1]) ? 0 : (int)$bits[1];;
120 if (!file_Exists($fn)) {
123 $fn = $sc->convertExists($fn, (int)$x, (int)$y);
126 //$this->target = $fn;
127 //$this->to = $toMimetype;
132 * actually run the convertion routine.
136 function convert($toMimetype, $x= 0, $y =0, $pg=false)
138 //print_R(func_get_args());
139 if ($toMimetype == 'image/jpg') {
140 $toMimetype = 'image/jpeg';
144 if(empty($pg) || is_nan($pg * 1)){
148 //echo '<PRE>'; print_r(array('convert', func_get_args()));
150 $toMimetype != $this->mimetype ||
152 $toMimetype == $this->mimetype &&
153 $toMimetype == 'image/gif'
157 $action = $this->getConvMethods($this->mimetype, $toMimetype);
159 //echo '<PRE>';print_r($action);
162 $this->debug("No methods found to convert {$this->mimetype} to {$toMimetype}");
165 $action->debug = $this->debug;
166 $fn = $action->runconvert($this->fn, $x, $y, $pg);
167 // delete the generated files after script execution
168 if(!empty(self::$options['delete_all'])) {
169 $this->deleteOnExitAdd($fn);
173 $this->to = $toMimetype;
174 $this->lastaction = $action->last ? $action->last : $action; // what failed.
178 // let's assume that conversions can handle scaling??
184 if (preg_match('#^image/#', $toMimetype) && $toMimetype != 'image/gif' && ( !empty($x) || !empty($y))) {
185 //var_dump(array($toMimetype));
187 require_once 'File/Convert/Solution.php';
188 $scf = (strpos($x, 'c') !== false ? 'scaleimagec' : 'scaleimage' );
189 require_once 'File/Convert/Solution/'. $scf . '.php';
190 $scls = 'File_Convert_Solution_' . $scf;
192 $sc = new $scls($toMimetype, $toMimetype);
193 $sc->debug= $this->debug;
194 $this->solutions[] = $sc;
195 $x = str_replace('c', 'x', $x);
197 if (strpos($x, 'x') !== false ) {
198 $bits = explode('x', $x);
200 $y = !is_numeric($bits[1]) ? '' : (int)$bits[1];
202 $x = strlen($x) ? (int) $x : '';
203 $y = strlen($y) ? (int) $y : '';
204 //print_r($x); print_r(' > '); print_r($y);exit;
206 $fn = $sc->runconvert($fn, $x, $y, $pg);
208 // delete the generated files after script execution
209 if(!empty(self::$options['delete_all'])) {
210 $this->deleteOnExitAdd($fn);
214 // print_r($this->target);
216 $this->to = $toMimetype;
222 function serveOnly($type=false, $filename =false, $delete_after = false)
224 $this->target = $this->fn;
225 $this->to = $this->mimetype;
226 $this->serve($type, $filename , $delete_after );
231 * Serve the file to a browser so it can be downloaded, or viewed.
233 * @param type string attachment or inline..
234 * @param filename string name of file
235 * @param delete_after boolean (false) delete file after sending..
238 function serve($type=false, $filename =false, $delete_after = false) // may die **/
240 if (empty($this->target)) {
241 // broken image? for images...
242 $cmd = isset($this->lastaction->cmd) ? $this->lastaction->cmd : "No Method";
243 die("not available in this format was: {$this->mimetype}, request: {$this->to}<BR>
244 Running - $cmd\n" . print_r(is_object($this->lastaction) ? $this->lastaction->log : '',true));
247 if (!file_exists($this->target))
249 trigger_error("Target does not exist: {$this->target}");
250 print_r($this->target);
257 $isIE = preg_match('#msie [0-9.]+#i', isset($_SERVER['HTTP_USER_AGENT']) ? isset($_SERVER['HTTP_USER_AGENT']) : '');
261 $ts = filemtime($fn);
263 $etag = md5($ts. '!' . $fn);
265 $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ?
266 trim($_SERVER['HTTP_IF_NONE_MATCH'],"'\"") : false;
268 $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ?
269 stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : false;
271 $ts_string = gmdate("D, d M Y H:i:s", $ts) . " GMT";
273 if ((($if_none_match && $if_none_match == $etag) || (!$if_none_match)) &&
274 ($if_modified_since && $if_modified_since == $ts_string))
276 header('HTTP/1.1 304 Not Modified');
281 //if (empty($_REQUEST['ts']) && !$isIE && $ifModifiedSince && strtotime($ifModifiedSince) >= $ts) {
282 // header('HTTP/1.0 304 Not Modified');
283 // exit; // stop processing
285 // ie kludge cause it's brain dead..
289 if ($isIE && preg_match('#^application/#i', $this->to)) {
290 // pdfs' break if we add this line?
291 //$mt = 'application/octet-stream' ;
292 $type = $type === false ? 'attachment' : $type;
294 $type = $type === false ? 'inline' : $type;
299 //if (!preg_match('#^image\/#i', $this->to)) {
300 $expires = isset(self::$options['expires']) ? self::$options['expires'] : "NOW + 5 MINUTES";
301 // a reasonable expiry time - 5 minutes..
302 header("Expires: ". gmdate("D, d M Y H:i:s", strtotime($expires)) . " GMT");
303 header("Cache-Control: must-revalidate");
304 header("Pragma: public");
305 header("Last-Modified: " . $ts_string . " GMT");
306 header("ETag: \"{$etag}\"");
309 require_once 'File/MimeType.php';
310 $fmt = new File_MimeType();
311 $ext = $fmt->toExt($mt);
312 $sfn = basename($fn);
313 $sfn = preg_match('#\.'.$ext.'$#', $sfn) ? $sfn : $sfn. '.' .$ext;
316 if (empty($filename)) {
320 $filename = preg_replace('/[^\x00-\x7E]/', '', $filename);
322 header('Content-length: '. filesize($fn));
323 // if ($type != 'inline') {
324 header('Content-Disposition: '.$type.'; filename="' . htmlspecialchars($filename). '"');
327 // needs to be removed after debugging - otherwise it logs to error.log
328 //ini_set('display_errors', 0); //trigger_error("Serving: {$this->target} ". filesize($fn));
329 if ($_SERVER["REQUEST_METHOD"] == 'HEAD') {
333 //var_dump($fn, $mt); exit;
334 header('Content-type: '. $mt);
336 // even though we have done a file_exists above - it still errors out here occausionally.
337 $fh = @fopen($fn, 'rb');
340 // passthrough seems to have problems -- trying fread
341 while($fh && !feof($fh))
343 echo @fread($fh, 1024*8);
366 cls => instance of class
374 static $methods = false;
375 if ($methods !== false ) {
379 $base = __DIR__.'/Convert/Solution';
380 $dh = opendir($base);
381 while (false !== ($fn = readdir($dh))) {
382 if (substr($fn,0,1) == '.' ) {
385 require_once 'File/Convert/Solution/' . $fn;
386 $cls = 'File_Convert_Solution_'. str_replace('.php', '',$fn);
388 $ref = new ReflectionClass($cls);
389 $val = $ref->getStaticPropertyValue('rules');
391 foreach($val as $r) {
408 * This recursively calls to find the best match.
409 * First by matching the 'from'
411 * Then if multiple outputs are available,
412 * It will see if any of those can be used to generate the to, by recurivly calling it'self..
416 function getConvMethods($from, $to, $stack = array())
418 // these source types have to use unoconv....
419 //print_r(array('getConvMethods', func_get_args()));
420 // $pos[converter] => array( list of targets);
421 require_once 'File/Convert/Solution.php';
423 if (count($stack) > 4) { // too deepp.. pos. recursion.
427 // print_r(self::$methods);
428 foreach($this->methods() as $t) {
429 if (!in_array($from, $t['from'])) {
432 if (in_array($to,$t['to'])) {
434 $ret = new $cls($from, $to); // found a solid match - returns the method.
435 //$ret->convert = $this; // recursion?
436 $this->solutions[] = $ret;
442 $pos[$t['cls']] = $t['to']; // list of targets
449 foreach($pos as $conv => $ar) {
450 // array contains a list of pos. mimetypes.
452 foreach($ar as $targ) {
453 if ($from == $targ) {
454 continue; // skip going back...
456 if (in_array($targ, $stack)) {
457 continue; // going backwards..
459 // we need to build a list here.
465 $try = $this->getConvMethods($targ, $to, $ns);
466 // try will be an array of method, from, to (or false)
468 if ($try === false) {
469 continue; // mo way to convert
471 // print_r($conv);exit;
473 $first = new $conv($from, $targ);
474 //$first->convert = $this;
475 $sol_list= $first->add($try);
483 $this->debug("No methods found to convert {$from} to {$to}");
487 // find the shortest..
488 usort ( $res , array($this, 'solutionSort'));
490 $this->solutions[] = $best;
495 function solutionSort($a, $b) {
496 if ($a->count() == $b->count()) {
499 return $a->count() < $b->count() ? -1 : 1;
506 if (is_callable($this->debug)) {
507 call_user_func($this->debug,$str);
509 echo $str . "<br/>\n";
515 static $deleteOnExit = false;
517 * generate a tempory file with an extension (dont forget to delete it)
520 function deleteOnExitAdd($name)
522 if (self::$deleteOnExit === false) {
523 register_shutdown_function(array('File_Convert','deleteOnExit'));
524 self::$deleteOnExit = array();
526 self::$deleteOnExit[] = $name;
529 static function deleteOnExit()
532 foreach(self::$deleteOnExit as $fn) {
533 if (file_exists($fn)) {