CWIS Developer Documentation
SPTImage.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: SPTImage.php
4 #
5 # Part of the Collection Workflow Integration System (CWIS)
6 # Copyright 2002-2013 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu/cwis/
8 #
9 
13 class SPTImage {
14 
15  # ---- PUBLIC INTERFACE --------------------------------------------------
16 
18  const IMAGE_PATH = "ImageStorage/";
20  const PREVIEW_PATH = "ImageStorage/Previews/";
22  const THUMBNAIL_PATH = "ImageStorage/Thumbnails/";
23 
36  function SPTImage($ImageIdOrFileNameOrImageObj,
37  $MaxWidth = NULL, $MaxHeight = NULL,
38  $MaxPreviewWidth = NULL, $MaxPreviewHeight = NULL,
39  $MaxThumbnailWidth = NULL, $MaxThumbnailHeight = NULL)
40  {
41  # clear error status (0 = AI_OKAY)
42  $this->ErrorStatus = 0;
43 
44  # trigger the Image class file to be autoloaded since some parts of this
45  # class (SPTImage) use constants defined in it but don't construct Image
46  # objects
47  new Image(NULL);
48 
49  # create and save a database handle for our use
50  $this->DB = new Database();
51 
52  # if image object was passed in
53  if (is_object($ImageIdOrFileNameOrImageObj)
54  && method_exists($ImageIdOrFileNameOrImageObj, "SPTImage"))
55  {
56  # create copy of image passed in
57  $this->CreateCopyOfImage($ImageIdOrFileNameOrImageObj);
58  }
59  # else if image ID was passed in
60  elseif (($ImageIdOrFileNameOrImageObj > 0)
61  && preg_match("/[0-9]+/", $ImageIdOrFileNameOrImageObj))
62  {
63  # load info on existing image
64  $this->LoadImageInfo($ImageIdOrFileNameOrImageObj);
65  }
66  # else assume that value passed in is file name
67  else
68  {
69  # create new image from named file
70  $this->CreateNewImage($ImageIdOrFileNameOrImageObj,
71  $MaxWidth, $MaxHeight,
72  $MaxPreviewWidth, $MaxPreviewHeight,
73  $MaxThumbnailWidth, $MaxThumbnailHeight);
74  }
75  }
76 
81  function Id() { return $this->Id; }
82 
87  function Url()
88  {
89  $Url = $this->FileName;
90  $SignalResult = $GLOBALS["AF"]->SignalEvent(
91  "EVENT_IMAGE_URL_FILTER", array(
92  "Url" => $Url,
93  "ImageSize" => "Full"));
94  $Url = $SignalResult["Url"];
95  return $Url;
96  }
97 
102  function PreviewUrl()
103  {
104  $Url = $this->PreviewFileName;
105  $SignalResult = $GLOBALS["AF"]->SignalEvent(
106  "EVENT_IMAGE_URL_FILTER", array(
107  "Url" => $Url,
108  "ImageSize" => "Preview"));
109  $Url = $SignalResult["Url"];
110  return $Url;
111  }
112 
117  function ThumbnailUrl()
118  {
119  $Url = $this->ThumbnailFileName;
120  $SignalResult = $GLOBALS["AF"]->SignalEvent(
121  "EVENT_IMAGE_URL_FILTER", array(
122  "Url" => $Url,
123  "ImageSize" => "Thumbnail"));
124  $Url = $SignalResult["Url"];
125  return $Url;
126  }
127 
133  function Format() { return $this->Format; }
134 
139  public function Mimetype()
140  {
141  $Image = new Image($this->FileName);
142  return $Image->Mimetype();
143  }
144 
149  function Height() { return $this->Height; }
150 
155  function Width() { return $this->Width; }
156 
161  function PreviewHeight() { return $this->PreviewHeight; }
162 
167  function PreviewWidth() { return $this->PreviewWidth; }
168 
173  function ThumbnailHeight() { return $this->ThumbnailHeight; }
174 
179  function ThumbnailWidth() { return $this->ThumbnailWidth; }
180 
185  static function ImageStorageDirectory()
186  {
187  # for each possible storage location
188  foreach (self::$ImageStorageLocations as $Dir)
189  {
190  # if location exists
191  if (is_dir($Dir))
192  {
193  # return location to caller
194  return $Dir;
195  }
196  }
197 
198  # return default (most preferred) location to caller
199  return self::$ImageStorageLocations[0];
200  }
201 
206  static function PreviewStorageDirectory()
207  {
208  # for each possible storage location
209  foreach (self::$PreviewStorageLocations as $Dir)
210  {
211  # if location exists
212  if (is_dir($Dir))
213  {
214  # return location to caller
215  return $Dir;
216  }
217  }
218 
219  # return default (most preferred) location to caller
220  return self::$PreviewStorageLocations[0];
221  }
222 
227  static function ThumbnailStorageDirectory()
228  {
229  # for each possible storage location
230  foreach (self::$ThumbnailStorageLocations as $Dir)
231  {
232  # if location exists
233  if (is_dir($Dir))
234  {
235  # return location to caller
236  return $Dir;
237  }
238  }
239 
240  # return default (most preferred) location to caller
241  return self::$ThumbnailStorageLocations[0];
242  }
243 
248  function GetLink() { return $this->FileName; }
249 
256  function AltText($NewValue = NULL)
257  {
258  # if new value supplied and new value differs from existing value
259  if (($NewValue !== NULL) && ($NewValue != $this->AltText))
260  {
261  # save new value to database
262  $this->DB->Query("UPDATE Images SET"
263  ." AltText = '".addslashes($NewValue)."'"
264  ." WHERE ImageId = ".$this->Id);
265 
266  # save new value locally
267  $this->AltText = $NewValue;
268  }
269 
270  # return attribute value to caller
271  return $this->AltText;
272  }
273 
278  function Delete()
279  {
280  # delete base image file
281  if (file_exists($this->FileName)) { unlink($this->FileName); }
282 
283  # delete preview image file
284  if (file_exists($this->PreviewFileName)) { unlink($this->PreviewFileName); }
285 
286  # delete thumbnail image file
287  if (file_exists($this->ThumbnailFileName)) { unlink($this->ThumbnailFileName); }
288 
289  # delete image info record in database
290  $this->DB->Query("DELETE FROM Images WHERE ImageId = ".$this->Id);
291  }
292 
297  function Status()
298  {
299  return $this->ErrorStatus;
300  }
301 
307  static function CheckDirectories()
308  {
309  # determine paths
310  $ImagePath = self::ImageStorageDirectory();
311  $PreviewPath = self::PreviewStorageDirectory();
312  $ThumbnailPath = self::ThumbnailStorageDirectory();
313 
314  # assume everything will be okay
315  $ErrorsFound = NULL;
316 
317  # check base image directory
318  if (!is_dir($ImagePath) || !is_writable($ImagePath))
319  {
320  if (!is_dir($ImagePath))
321  {
322  @mkdir($ImagePath, 0755);
323  }
324  else
325  {
326  @chmod($ImagePath, 0755);
327  }
328  if (!is_dir($ImagePath))
329  {
330  $ErrorsFound[] = "Image Storage Directory Not Found";
331  }
332  elseif (!is_writable($ImagePath))
333  {
334  $ErrorsFound[] = "Image Storage Directory Not Writable";
335  }
336  }
337 
338  # check preview directory
339  if (!is_dir($PreviewPath) || !is_writable($PreviewPath))
340  {
341  if (!is_dir($PreviewPath))
342  {
343  @mkdir($PreviewPath, 0755);
344  }
345  else
346  {
347  @chmod($PreviewPath, 0755);
348  }
349  if (!is_dir($PreviewPath))
350  {
351  $ErrorsFound[] = "Preview Storage Directory Not Found";
352  }
353  elseif (!is_writable($PreviewPath))
354  {
355  $ErrorsFound[] = "Preview Storage Directory Not Writable";
356  }
357  }
358 
359  # check thumbnail directory
360  if (!is_dir($ThumbnailPath) || !is_writable($ThumbnailPath))
361  {
362  if (!is_dir($ThumbnailPath))
363  {
364  @mkdir($ThumbnailPath, 0755);
365  }
366  else
367  {
368  @chmod($ThumbnailPath, 0755);
369  }
370  if (!is_dir($ThumbnailPath))
371  {
372  $ErrorsFound[] = "Thumbnail Storage Directory Not Found";
373  }
374  elseif (!is_writable($ThumbnailPath))
375  {
376  $ErrorsFound[] = "Thumbnail Storage Directory Not Writable";
377  }
378  }
379 
380  # return any errors found to caller
381  return $ErrorsFound;
382  }
383 
394  public function Resize($MaxWidth, $MaxHeight,
395  $MaxPreviewWidth, $MaxPreviewHeight,
396  $MaxThumbnailWidth, $MaxThumbnailHeight)
397  {
398  $SrcImage = new Image($this->FileName);
399 
400  # scale the original image if necessary
401  $MaxWidth = min($MaxWidth, $SrcImage->XSize());
402  $MaxHeight = min($MaxHeight, $SrcImage->YSize());
403  $SrcImage->ScaleTo($MaxWidth, $MaxHeight, TRUE);
404 
405  # save and reload image info
406  $SrcImage->SaveAs($this->FileName);
407  $SrcImage = new Image($this->FileName);
408 
409  # retrieve image width and height
410  $this->Height = $SrcImage->YSize();
411  $this->Width = $SrcImage->XSize();
412 
413  # generate preview image and calculate width and height
414  $MaxPreviewWidth = min($MaxPreviewWidth, $this->Width);
415  $MaxPreviewHeight = min($MaxPreviewHeight, $this->Height);
416  $SrcImage->ScaleTo($MaxPreviewWidth, $MaxPreviewHeight, TRUE);
417  $SrcImage->SaveAs($this->PreviewFileName);
418  if (($this->Width * $MaxPreviewHeight)
419  > ($this->Height * $MaxPreviewWidth))
420  {
421  $this->PreviewWidth = $MaxPreviewWidth;
422  $this->PreviewHeight =
423  ($MaxPreviewWidth * $SrcImage->YSize()) / $SrcImage->XSize();
424  }
425  else
426  {
427  $this->PreviewWidth =
428  ($MaxPreviewHeight * $SrcImage->XSize()) / $SrcImage->YSize();
429  $this->PreviewHeight = $MaxPreviewHeight;
430  }
431 
432  # generate thumbnail image and calculate width and height
433  $MaxThumbnailWidth = min($MaxThumbnailWidth, $this->Width);
434  $MaxThumbnailHeight = min($MaxThumbnailHeight, $this->Height);
435  $SrcImage->ScaleTo($MaxThumbnailWidth, $MaxThumbnailHeight, TRUE);
436  $SrcImage->SaveAs($this->ThumbnailFileName);
437  if (($this->Width * $MaxThumbnailHeight)
438  > ($this->Height * $MaxThumbnailWidth))
439  {
440  $this->ThumbnailWidth = $MaxThumbnailWidth;
441  $this->ThumbnailHeight =
442  ($MaxThumbnailWidth * $SrcImage->YSize()) / $SrcImage->XSize();
443  }
444  else
445  {
446  $this->ThumbnailWidth = ($MaxThumbnailHeight * $SrcImage->XSize()) / $SrcImage->YSize();
447  $this->ThumbnailHeight = $MaxThumbnailHeight;
448  }
449 
450  # save image attributes to database
451  $this->SaveImageInfo();
452  }
453 
454  # ---- PRIVATE INTERFACE -------------------------------------------------
455 
456  private $Id;
457  private $FileName;
458  private $PreviewFileName;
459  private $ThumbnailFileName;
460  private $Format;
461  private $AltText;
462  private $Url;
463  private $PreviewUrl;
464  private $ThumbnailUrl;
465  private $Height;
466  private $Width;
467  private $PreviewHeight;
468  private $PreviewWidth;
469  private $ThumbnailHeight;
470  private $ThumbnailWidth;
471  private $DB;
472  private $ErrorStatus;
473 
475  static private $ImageStorageLocations = array(
476  "local/data/images",
477  "ImageStorage",
478  );
479  static private $PreviewStorageLocations = array(
480  "local/data/images/previews",
481  "ImageStorage/Previews",
482  );
483  static private $ThumbnailStorageLocations = array(
484  "local/data/images/thumbnails",
485  "ImageStorage/Thumbnails",
486  );
487 
499  private function CreateNewImage($FileName, $MaxWidth, $MaxHeight,
500  $MaxPreviewWidth, $MaxPreviewHeight, $MaxThumbnailWidth, $MaxThumbnailHeight)
501  {
502  # if file does not exist or is not readable
503  if (!is_readable($FileName))
504  {
505  # set error status
506  $this->ErrorStatus = AI_FILEUNREADABLE;
507  }
508  else
509  {
510  # if image is invalid or unsupported type
511  $SrcImage = new Image($FileName);
512  $this->Format = $SrcImage->Type();
513  if ($SrcImage->Status() != AI_OKAY)
514  {
515  # set error status
516  $this->ErrorStatus = $SrcImage->Status();
517  }
518  else
519  {
520  # generate new image ID
521  $this->Id = $this->GenerateNewImageId();
522 
523  # generate and set file names
524  $this->SetFileNames();
525 
526  # if our image file name differs from file name passed in
527  if (realpath($this->FileName) != realpath($FileName))
528  {
529  # create image file
530  $SrcImage->SaveAs($this->FileName);
531 
532  # if create failed set error status and bail out
533  if ($SrcImage->Status() != AI_OKAY)
534  {
535  $this->DB->Query("DELETE FROM Images WHERE ImageId = "
536  .intval($this->Id));
537  $this->ErrorStatus = $SrcImage->Status();
538  return;
539  }
540  }
541 
542  # scale the original image if necessary
543  $MaxWidth = min($MaxWidth, $SrcImage->XSize());
544  $MaxHeight = min($MaxHeight, $SrcImage->YSize());
545 
546  # change the minimum width if the height is the limiting factor
547  if ($SrcImage->YSize() * $MaxWidth / $SrcImage->XSize() > $MaxHeight)
548  {
549  $MaxWidth = round($SrcImage->XSize() * $MaxHeight / $SrcImage->YSize());
550  }
551 
552  # change the minimum height since the width is the limiting factor
553  else
554  {
555  $MaxHeight = round($SrcImage->YSize() * $MaxWidth / $SrcImage->XSize());
556  }
557 
558  # scale the image
559  $SrcImage->ScaleTo($MaxWidth, $MaxHeight, TRUE);
560 
561  # save and reload image info
562  $SrcImage->SaveAs($this->FileName);
563  $SrcImage = new Image($this->FileName);
564 
565  # retrieve image width and height
566  $this->Height = $SrcImage->YSize();
567  $this->Width = $SrcImage->XSize();
568 
569  # create the preview and thumbnail images
570  foreach (array("Preview", "Thumbnail") as $ImageType)
571  {
572  # variable name strings to use in the variable variables below
573  $MaxWidthVar = "Max".$ImageType."Width";
574  $MaxHeightVar = "Max".$ImageType."Height";
575 
576  # find the mininum values for the width and height
577  $$MaxWidthVar = min($$MaxWidthVar, $this->Width);
578  $$MaxHeightVar= min($$MaxHeightVar, $this->Height);
579 
580  # change the minimum width if the height is the limiting factor
581  if ($this->Height * $$MaxWidthVar / $this->Width > $$MaxHeightVar)
582  {
583  $$MaxWidthVar =
584  round($this->Width * $$MaxHeightVar / $this->Height);
585  }
586 
587  # change the minimum height since the width is the limiting factor
588  else
589  {
590  $$MaxHeightVar =
591  round($this->Height * $$MaxWidthVar / $this->Width);
592  }
593 
594  # scale the image and save it to a new file
595  $SrcImage->ScaleTo($$MaxWidthVar, $$MaxHeightVar, TRUE);
596  $SrcImage->SaveAs($this->{$ImageType."FileName"});
597 
598  # scaling/saving failed
599  if ($SrcImage->Status() != AI_OKAY)
600  {
601  $this->DB->Query("DELETE FROM Images WHERE ImageId = "
602  .intval($this->Id));
603  $this->ErrorStatus = $SrcImage->Status();
604  return;
605  }
606 
607  # save the dimensions
608  $this->{$ImageType."Width"} = $$MaxWidthVar;
609  $this->{$ImageType."Height"} = $$MaxHeightVar;
610  }
611 
612  # save image attributes to database
613  $this->SaveImageInfo();
614  }
615  }
616  }
617 
622  private function LoadImageInfo($ImageId)
623  {
624  # save image ID
625  $this->Id = $ImageId;
626 
627  # load image record from database
628  $this->DB->Query("SELECT * FROM Images WHERE ImageId = ".$ImageId);
629 
630  # if the ID is invalid
631  if (!$this->DB->NumRowsSelected())
632  {
633  $this->ErrorStatus = AI_INTERNALERROR;
634  return;
635  }
636 
637  $Record = $this->DB->FetchRow();
638 
639  # load in values from record
640  $this->Format = $Record["Format"];
641  $this->AltText = $Record["AltText"];
642  $this->Height = $Record["Height"];
643  $this->Width = $Record["Width"];
644  $this->PreviewHeight = $Record["PreviewHeight"];
645  $this->PreviewWidth = $Record["PreviewWidth"];
646  $this->ThumbnailHeight = $Record["ThumbnailHeight"];
647  $this->ThumbnailWidth = $Record["ThumbnailWidth"];
648 
649  # generate file names
650  $this->SetFileNames();
651  }
652 
657  private function CreateCopyOfImage($SrcImage)
658  {
659  $Image = new Image($SrcImage->Url());
660  if ($Image->Status() != AI_OKAY)
661  {
662  # set error status
663  $this->ErrorStatus = $Image->Status();
664  return;
665  }
666 
667  # generate new image ID
668  $this->Id = $this->GenerateNewImageId();
669 
670  # generate file names
671  $this->SetFileNames();
672 
673  # copy attributes from source image
674  $this->Format = $SrcImage->Format();
675  $this->AltText = $SrcImage->AltText();
676  $this->Width = $SrcImage->Width();
677  $this->Height = $SrcImage->Height();
678  $this->PreviewWidth = $SrcImage->PreviewWidth();
679  $this->PreviewHeight = $SrcImage->PreviewHeight();
680  $this->ThumbnailWidth = $SrcImage->ThumbnailWidth();
681  $this->ThumbnailHeight = $SrcImage->ThumbnailHeight();
682 
683  # copy source image files
684  copy($SrcImage->Url(), $this->FileName);
685  copy($SrcImage->PreviewUrl(), $this->PreviewFileName);
686  copy($SrcImage->ThumbnailUrl(), $this->ThumbnailFileName);
687 
688  # save image attributes to database
689  $this->SaveImageInfo();
690  }
691 
696  private function SetFileNames()
697  {
698  if (Image::Extension($this->Format))
699  {
700  $FileExtension = Image::Extension($this->Format);
701  }
702  else
703  {
704  $FileExtension = "";
705  }
706 
707  $this->FileName = $this->DetermineFileName(
708  self::$ImageStorageLocations, "Img--", $FileExtension);
709  $this->PreviewFileName = $this->DetermineFileName(
710  self::$PreviewStorageLocations, "Preview--", $FileExtension);
711  $this->ThumbnailFileName = $this->DetermineFileName(
712  self::$ThumbnailStorageLocations, "Thumb--", $FileExtension);
713  }
714 
724  private function DetermineFileName($Locations, $Prefix, $Extension)
725  {
726  # build base name for file
727  $BaseName = $Prefix.sprintf("%08d.", $this->Id).$Extension;
728 
729  # for each possible location
730  foreach ($Locations as $Dir)
731  {
732  # build full file name for location
733  $FileName = $Dir."/".$BaseName;
734 
735  # if file exists in location return full file name
736  if (file_exists($FileName)) { return $FileName; }
737  }
738 
739  # for each possible location
740  foreach ($Locations as $Dir)
741  {
742  # build full file name for location
743  $FileName = $Dir."/".$BaseName;
744 
745  # if location is writable return full file name
746  if (is_dir($Dir) && is_writable($Dir)) { return $FileName; }
747  }
748 
749  # return full file name for default location
750  return $Locations[0]."/".$BaseName;
751  }
752 
757  private function GenerateNewImageId()
758  {
759  # add new entry to database
760  $this->DB->Query("INSERT INTO Images (AltText) VALUES ('')");
761 
762  # return ID of inserted image
763  return $this->DB->LastInsertId();
764  }
765 
769  private function SaveImageInfo()
770  {
771  # update existing image record
772  $this->DB->Query("UPDATE Images SET"
773  ." Format = '" .$this->Format."',"
774  ." AltText = '" .addslashes($this->AltText)."',"
775  ." Height = '" .$this->Height."',"
776  ." Width = '" .$this->Width."',"
777  ." PreviewHeight = '" .$this->PreviewHeight."',"
778  ." PreviewWidth = '" .$this->PreviewWidth."',"
779  ." ThumbnailHeight = '".$this->ThumbnailHeight."',"
780  ." ThumbnailWidth = '" .$this->ThumbnailWidth."'"
781  ." WHERE ImageId = ".$this->Id);
782  }
783 
784 }
Mimetype()
Get the MIME type for the image.
Definition: SPTImage.php:139
ThumbnailWidth()
Get the width of the thumbnail image for this image.
Definition: SPTImage.php:179
SPTImage($ImageIdOrFileNameOrImageObj, $MaxWidth=NULL, $MaxHeight=NULL, $MaxPreviewWidth=NULL, $MaxPreviewHeight=NULL, $MaxThumbnailWidth=NULL, $MaxThumbnailHeight=NULL)
Object constructor.
Definition: SPTImage.php:36
SQL database abstraction object with smart query caching.
Delete()
Delete the image, that is, remove its record from the database and delete the associated image files ...
Definition: SPTImage.php:278
PreviewHeight()
Get the height of the preview image for this image.
Definition: SPTImage.php:161
const THUMBNAIL_PATH
path where thumbnail images are stored
Definition: SPTImage.php:22
const AI_FILEUNREADABLE
PreviewWidth()
Get the width of the preview image for this image.
Definition: SPTImage.php:167
Status()
Get the error status set by the constructor.
Definition: SPTImage.php:297
const AI_INTERNALERROR
static CheckDirectories()
Check that the image storage directories are available, creating them and attempting to change their ...
Definition: SPTImage.php:307
ThumbnailHeight()
Get the height of the thumbnail image for this image.
Definition: SPTImage.php:173
Width()
Get the width of the image.
Definition: SPTImage.php:155
Encapsulates a full-size, preview, and thumbnail image.
Definition: SPTImage.php:13
Url()
Get the path to the image.
Definition: SPTImage.php:87
static PreviewStorageDirectory()
Get the path to the preview image storage directory.
Definition: SPTImage.php:206
PreviewUrl()
Get the path to the preview image for this image.
Definition: SPTImage.php:102
Format()
Get the format of the image.
Definition: SPTImage.php:133
const PREVIEW_PATH
path where preview images are stored
Definition: SPTImage.php:20
GetLink()
Get the path to the full-size image.
Definition: SPTImage.php:248
Id()
Get the ID of the image in the database.
Definition: SPTImage.php:81
const AI_OKAY
Height()
Get the height of the image.
Definition: SPTImage.php:149
Resize($MaxWidth, $MaxHeight, $MaxPreviewWidth, $MaxPreviewHeight, $MaxThumbnailWidth, $MaxThumbnailHeight)
Resize the full-size, preview, and thumbnail images based on the given dimension restrictions.
Definition: SPTImage.php:394
AltText($NewValue=NULL)
Get or set the alternate text value for the image.
Definition: SPTImage.php:256
const IMAGE_PATH
base path where images are stored
Definition: SPTImage.php:18
static ThumbnailStorageDirectory()
Get the path to the thumbnail image storage directory.
Definition: SPTImage.php:227
static ImageStorageDirectory()
Get the path to the (full-size) image storage directory.
Definition: SPTImage.php:185
ThumbnailUrl()
Get the path to the thumbnail image for this image.
Definition: SPTImage.php:117
static Extension($Type=NULL)