initial commit
initial commit

file:b/composer.json (new)
--- /dev/null
+++ b/composer.json
@@ -1,1 +1,31 @@
+{
+    "name": "photo collage",
+    "description": "create photo collages with php",
+    "homepage": "http://git.razvi.ro",
+    "type": "library",
+    "license": "private",
+    "authors": [
+        {
+            "name": "Razvan Stanga",
+            "email": "git@razvi.ro"
+        }
+    ],
+    "support": {
+        "email": "git@razvi.ro"
+    },
+    "require": {
+        "php": ">=5.4.16"
+    },
+    "require-dev": {
+    },
+    "autoload": {
+        "psr-4": {
+            "razvanstanga\\photocollage": "src"
+        }
+    },
+    "autoload-dev": {
+    },
+    "minimum-stability": "dev",
+    "prefer-stable": true
+}
 

--- /dev/null
+++ b/src/PhotoCollage.php
@@ -1,1 +1,346 @@
-
+<?php
+
+namespace razvanstanga\photocollage;
+
+/**
+ *  Photo collage
+ */
+
+class PhotoCollage {
+    protected $background = [];
+    protected $photos = [];
+    protected $texts = [];
+    protected $allowedExtensions = ["png", "jpg", "jpeg", "gif", "bmp"];
+
+    /**
+     * Create the photo collage
+     *
+     * @param string $path photo path
+     * @param string $filename photo filename
+     * @return string filename
+     */
+    public function create($path, $filename)
+    {
+        $background  = imagecreatetruecolor($this->background['width'], $this->background['height']);
+        if ($this->background['transparent']) {
+            imagesavealpha($background, true);
+            $trans_colour = imagecolorallocatealpha($background, 0, 0, 0, 127);
+            imagefill($background, 0, 0, $trans_colour);
+        }
+        foreach ($this->photos as $photo) {
+            if (file_exists($photo['path']) && $photo['path']) {
+                $_imageData = $this->checkImage($photo['path']);
+                if ($photo['resize'] && $photo['size']) {
+                    $_imageData['image'] = $this->processImage($_imageData['image'], $_imageData['width'], $_imageData['height'], $photo['size'], $photo['resize']);
+                }
+                imagecopyresampled($background, $_imageData['image'], $photo['left'], $photo['top'], 0, 0, $photo['width'], $photo['height'], $photo['width'], $photo['height']);
+            } elseif(filter_var($photo['path'], FILTER_VALIDATE_URL)) {
+                $_image = file_get_contents($photo['path']);
+                $_image = imagecreatefromstring ($_image);
+                if ($_image && $photo['resize'] && $photo['size']) {
+                    $_image = $this->processImage($_image, null, null, $photo['size'], $photo['resize']);
+                }
+                if ($_image) {
+                    imagecopyresampled($background, $_image, $photo['left'], $photo['top'], 0, 0, $photo['width'], $photo['height'], $photo['width'], $photo['height']);
+                }
+            }
+        }
+        foreach ($this->texts as $text) {
+            if (isset($text['wrap'])) {
+                $textLines = $this->wrapText ($text['text'], $text['wrap']);
+                foreach ( $textLines as $line => $_text ) {
+                    $charDetails = imageftbbox ($text['size'], 0, $text['font'], $_text, array());
+                    $_width = abs($charDetails[0]) + abs($charDetails[2]);
+                    $_height = abs($charDetails[1]) + abs($charDetails[5]);
+                    if ($text['center']) {
+                        $text['left'] = intval($text['left'] - $_width/2);
+                    }
+                    $color = imagecolorallocate($background, $text['color']['r'], $text['color']['g'], $text['color']['b']);
+                    imagefttext ($background, $text['size'], 0, $text['left'], $text['top'], $color, $text['font'], $_text);
+                    $text['top'] += $_height + 3;
+                }
+            } else {
+                $charDetails = imageftbbox ($text['size'], 0, $text['font'], $text['text'], []);
+                $_width = abs($charDetails[0]) + abs($charDetails[2]);
+                $_height = abs($charDetails[1]) + abs($charDetails[5]);
+                if ($text['center']) {
+                    $text['left'] = intval($text['left'] - $_width/2);
+                }
+                $color = imagecolorallocate($background, $text['color']['r'], $text['color']['g'], $text['color']['b']);
+                imagefttext ($background, $text['size'], 0, $text['left'], $text['top'], $color, $text['font'], $text['text']);
+            }
+        }
+
+        imagepng($background, $path . $filename, 9);
+
+        return $filename;
+    }
+
+    /**
+     * Photo background
+     *
+     * @param int  $width photo width
+     * @param int  $height photo height
+     * @param boolean $transparent transparent background
+     */
+    public function setBackground($width, $height, $transparent = false)
+    {
+        $this->background = ['width' => $width, 'height' => $height, 'transparent' => $transparent];
+    }
+
+    /**
+     * Adds photo to collage
+     *
+     * @param string $path path to photo
+     * @param array  $position photo position in collage
+     * @param array  $size photo size
+     * @param array  $resize photo resize details
+     */
+    public function addPhoto($path, array $position, array $size, array $resize = [])
+    {
+        $this->photos[] = ['path' => $path, 'top' => $position['top'], 'left' => $position['left'], 'width' => $size['width'], 'height' => $size['height'], 'resize' => $resize['resize'], 'size' => $resize['size']];
+    }
+
+    /**
+     * Adds text to collage
+     *
+     * @param string $text text
+     * @param string $font path to font
+     * @param int $size font size
+     * @param array  $position text position
+     * @param array  $color text rgb color
+     */
+    public function addText($text, $font, $size, array $position, array $color)
+    {
+        $this->texts[] = ['text' => $text, 'font' => $font, 'size' => $size, 'left' => $position['left'], 'top' => $position['top'], 'center' => $position['center'], 'wrap' => $position['wrap'], 'color' => $color];
+    }
+
+    /**
+     * Resizes image
+     *
+     * @param  image $srcImage image source
+     * @param  int $srcWidth image source width
+     * @param  int $srcHeight image source height
+     * @param  int $newSize resize to newsize
+     * @param  string $resize resize by "bigger", "smaller", "width", "height"
+     * @return image resized image
+     */
+    public function processImage($srcImage, $srcWidth, $srcHeight, $newSize, $resize = "bigger")
+    {
+        $resizes = ["bigger", "smaller", "width", "height"];
+        if ( !in_array ($resize, $resizes) ) {
+            $resize = "bigger";
+        }
+        $srcWidth = $srcWidth ? $srcWidth : imagesx($srcImage);
+        $srcHeight = $srcHeight ? $srcHeight : imagesy($srcImage);
+        if ($resize == "bigger") {
+            if ($srcWidth >= $srcHeight) {
+                $resize = "width";
+            } else {
+                $resize = "height";
+            }
+        } elseif ($resize == "smaller") {
+            if ($srcWidth <= $srcHeight) {
+                $resize = "width";
+            } else {
+                $resize = "height";
+            }
+        }
+
+        if ($resize == "width") {
+            $destWidth = $newSize;
+            $destHeight  = ceil($destWidth / ($srcWidth / $srcHeight));
+        } elseif ( $resize == "height" ) {
+            $destHeight = $newSize;
+            $destWidth  = ceil($destHeight / ($srcHeight/$srcWidth));
+        }
+
+        $crop = [];
+        $crop['x'] = 0;
+        $crop['y'] = 0;
+        $crop['w'] = $srcWidth;
+        $crop['h'] = $srcHeight;
+
+        $destImage = imagecreatetruecolor($destWidth, $destHeight);
+        imagecopyresampled($destImage, $srcImage, 0, 0, $crop['x'], $crop['y'], $destWidth, $destHeight, $crop['w'], $crop['h']);
+        imagedestroy($srcImage);
+
+        return $destImage;
+    }
+
+    /**
+     * Validates image
+     *
+     * @param  string $file path to file
+     * @return array|bool
+     */
+    public function checkImage($file)
+    {
+        $ext = $this->getFileext($file);
+        if (!in_array($ext, $this->allowedExtensions)) {
+            return false;
+        }
+        $size = getimagesize($file);
+        $size['ext'] = $ext;
+        if ( !is_array ($size) || $size[0] < 1 || $size[1] < 2 ) {
+            return false;
+        }
+        if ($ext == "jpg" || $ext == "jpeg") {
+            $image = @imagecreatefromjpeg($file);
+        } elseif ($ext == "gif") {
+            $image = @imagecreatefromgif($file);
+        } elseif ($ext == "png") {
+            $image = @imagecreatefrompng($file);
+        } elseif ($ext == "bmp") {
+            $image = $this->imagecreatefrombmp($file);
+        }
+        if ($image == false) {
+            return false;
+        }
+        $size['image'] = $image;
+        return $size;
+    }
+
+    /**
+     * Get file extension
+     *
+     * @param  string $file path to file
+     * @return string extension
+     */
+    public function getFileext($file)
+    {
+        return strtolower(substr(strrchr($file,"."),1));
+    }
+
+    /**
+     * [wrapText description]
+     * @param  string  $text text wo wrap
+     * @param  integer $chars wrap to chars
+     * @return array
+     */
+    public function wrapText($text, $chars = 42) {
+        return explode ("\n", wordwrap($text, $chars, "\n", true));
+    }
+
+    /**
+     * Creates image instance from .bmp files
+     *
+     * @param  string $filename path to file
+     * @return image|bool
+     */
+    public function imagecreatefrombmp($filename) {
+        $tmp_name = tempnam("/tmp", "GD_" . uniqid());
+        if($this->ConvertBMP2GD($filename, $tmp_name)) {
+            $img = imagecreatefromgd($tmp_name);
+            unlink($tmp_name);
+            return $img;
+        }
+        return false;
+    }
+
+    /**
+     * ConvertBMP2GD
+     *
+     * @param image  $src  image instance
+     * @param boolean $dest temporary file
+     */
+    protected function ConvertBMP2GD($src, $dest = false)
+    {
+        if(!($src_f = fopen($src, "rb"))) {
+            return false;
+        }
+        if(!($dest_f = fopen($dest, "wb"))) {
+            return false;
+        }
+        $header = unpack("vtype/Vsize/v2reserved/Voffset", fread($src_f, 14));
+        $info = unpack("Vsize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vncolor/Vimportant",
+        fread($src_f, 40));
+        extract($info);
+        extract($header);
+        if($type != 0x4D42) { // signature "BM"
+            return false;
+        }
+
+        $palette_size = $offset - 54;
+        $ncolor = $palette_size / 4;
+        $gd_header = "";
+        // true-color vs. palette
+        $gd_header .= ($palette_size == 0) ? "\xFF\xFE" : "\xFF\xFF";
+        $gd_header .= pack("n2", $width, $height);
+        $gd_header .= ($palette_size == 0) ? "\x01" : "\x00";
+        if($palette_size) {
+            $gd_header .= pack("n", $ncolor);
+        }
+        // no transparency
+        $gd_header .= "\xFF\xFF\xFF\xFF";
+
+        fwrite($dest_f, $gd_header);
+
+        if($palette_size) {
+            $palette = fread($src_f, $palette_size);
+            $gd_palette = "";
+            $j = 0;
+            while($j < $palette_size) {
+                $b = $palette{$j++};
+                $g = $palette{$j++};
+                $r = $palette{$j++};
+                $a = $palette{$j++};
+                $gd_palette .= "$r$g$b$a";
+            }
+            $gd_palette .= str_repeat("\x00\x00\x00\x00", 256 - $ncolor);
+            fwrite($dest_f, $gd_palette);
+        }
+
+        $scan_line_size = (($bits * $width) + 7) >> 3;
+        $scan_line_align = ($scan_line_size & 0x03) ? 4 - ($scan_line_size & 0x03) : 0;
+
+        for($i = 0, $l = $height - 1; $i < $height; $i++, $l--) {
+            // BMP stores scan lines starting from bottom
+            fseek($src_f, $offset + (($scan_line_size + $scan_line_align) * $l));
+            $scan_line = fread($src_f, $scan_line_size);
+            if($bits == 24) {
+                $gd_scan_line = "";
+                $j = 0;
+                while($j < $scan_line_size) {
+                    $b = $scan_line{$j++};
+                    $g = $scan_line{$j++};
+                    $r = $scan_line{$j++};
+                    $gd_scan_line .= "\x00$r$g$b";
+                }
+            } else if($bits == 8) {
+                $gd_scan_line = $scan_line;
+            } else if($bits == 4) {
+                $gd_scan_line = "";
+                $j = 0;
+                while($j < $scan_line_size) {
+                    $byte = ord($scan_line{$j++});
+                    $p1 = chr($byte >> 4);
+                    $p2 = chr($byte & 0x0F);
+                    $gd_scan_line .= "$p1$p2";
+                } $gd_scan_line = substr($gd_scan_line, 0, $width);
+            } else if($bits == 1) {
+                $gd_scan_line = "";
+                $j = 0;
+                while($j < $scan_line_size) {
+                    $byte = ord($scan_line{$j++});
+                    $p1 = chr((int) (($byte & 0x80) != 0));
+                    $p2 = chr((int) (($byte & 0x40) != 0));
+                    $p3 = chr((int) (($byte & 0x20) != 0));
+                    $p4 = chr((int) (($byte & 0x10) != 0));
+                    $p5 = chr((int) (($byte & 0x08) != 0));
+                    $p6 = chr((int) (($byte & 0x04) != 0));
+                    $p7 = chr((int) (($byte & 0x02) != 0));
+                    $p8 = chr((int) (($byte & 0x01) != 0));
+                    $gd_scan_line .= "$p1$p2$p3$p4$p5$p6$p7$p8";
+                }
+                $gd_scan_line = substr($gd_scan_line, 0, $width);
+            }
+
+            fwrite($dest_f, $gd_scan_line);
+        }
+        fclose($src_f);
+        fclose($dest_f);
+        return true;
+    }
+}
+

comments