Subversion-Projekte lars-tiefland.ci

Revision

Revision 2049 | Revision 2242 | Zur aktuellen Revision | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
68 lars 1
<?php
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
2049 lars 9
 * Copyright (c) 2014 - 2017, British Columbia Institute of Technology
68 lars 10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
2049 lars 32
 * @copyright	Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
68 lars 33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	https://codeigniter.com
35
 * @since	Version 1.0.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
 
40
/**
41
 * Image Manipulation class
42
 *
43
 * @package		CodeIgniter
44
 * @subpackage	Libraries
45
 * @category	Image_lib
46
 * @author		EllisLab Dev Team
47
 * @link		https://codeigniter.com/user_guide/libraries/image_lib.html
48
 */
49
class CI_Image_lib {
50
 
51
	/**
52
	 * PHP extension/library to use for image manipulation
53
	 * Can be: imagemagick, netpbm, gd, gd2
54
	 *
55
	 * @var string
56
	 */
57
	public $image_library		= 'gd2';
58
 
59
	/**
60
	 * Path to the graphic library (if applicable)
61
	 *
62
	 * @var string
63
	 */
64
	public $library_path		= '';
65
 
66
	/**
67
	 * Whether to send to browser or write to disk
68
	 *
69
	 * @var bool
70
	 */
71
	public $dynamic_output		= FALSE;
72
 
73
	/**
74
	 * Path to original image
75
	 *
76
	 * @var string
77
	 */
78
	public $source_image		= '';
79
 
80
	/**
81
	 * Path to the modified image
82
	 *
83
	 * @var string
84
	 */
85
	public $new_image		= '';
86
 
87
	/**
88
	 * Image width
89
	 *
90
	 * @var int
91
	 */
92
	public $width			= '';
93
 
94
	/**
95
	 * Image height
96
	 *
97
	 * @var int
98
	 */
99
	public $height			= '';
100
 
101
	/**
102
	 * Quality percentage of new image
103
	 *
104
	 * @var int
105
	 */
106
	public $quality			= 90;
107
 
108
	/**
109
	 * Whether to create a thumbnail
110
	 *
111
	 * @var bool
112
	 */
113
	public $create_thumb		= FALSE;
114
 
115
	/**
116
	 * String to add to thumbnail version of image
117
	 *
118
	 * @var string
119
	 */
120
	public $thumb_marker		= '_thumb';
121
 
122
	/**
123
	 * Whether to maintain aspect ratio when resizing or use hard values
124
	 *
125
	 * @var bool
126
	 */
127
	public $maintain_ratio		= TRUE;
128
 
129
	/**
130
	 * auto, height, or width.  Determines what to use as the master dimension
131
	 *
132
	 * @var string
133
	 */
134
	public $master_dim		= 'auto';
135
 
136
	/**
137
	 * Angle at to rotate image
138
	 *
139
	 * @var string
140
	 */
141
	public $rotation_angle		= '';
142
 
143
	/**
144
	 * X Coordinate for manipulation of the current image
145
	 *
146
	 * @var int
147
	 */
148
	public $x_axis			= '';
149
 
150
	/**
151
	 * Y Coordinate for manipulation of the current image
152
	 *
153
	 * @var int
154
	 */
155
	public $y_axis			= '';
156
 
157
	// --------------------------------------------------------------------------
158
	// Watermark Vars
159
	// --------------------------------------------------------------------------
160
 
161
	/**
162
	 * Watermark text if graphic is not used
163
	 *
164
	 * @var string
165
	 */
166
	public $wm_text			= '';
167
 
168
	/**
169
	 * Type of watermarking.  Options:  text/overlay
170
	 *
171
	 * @var string
172
	 */
173
	public $wm_type			= 'text';
174
 
175
	/**
176
	 * Default transparency for watermark
177
	 *
178
	 * @var int
179
	 */
180
	public $wm_x_transp		= 4;
181
 
182
	/**
183
	 * Default transparency for watermark
184
	 *
185
	 * @var int
186
	 */
187
	public $wm_y_transp		= 4;
188
 
189
	/**
190
	 * Watermark image path
191
	 *
192
	 * @var string
193
	 */
194
	public $wm_overlay_path		= '';
195
 
196
	/**
197
	 * TT font
198
	 *
199
	 * @var string
200
	 */
201
	public $wm_font_path		= '';
202
 
203
	/**
204
	 * Font size (different versions of GD will either use points or pixels)
205
	 *
206
	 * @var int
207
	 */
208
	public $wm_font_size		= 17;
209
 
210
	/**
211
	 * Vertical alignment:   T M B
212
	 *
213
	 * @var string
214
	 */
215
	public $wm_vrt_alignment	= 'B';
216
 
217
	/**
218
	 * Horizontal alignment: L R C
219
	 *
220
	 * @var string
221
	 */
222
	public $wm_hor_alignment	= 'C';
223
 
224
	/**
225
	 * Padding around text
226
	 *
227
	 * @var int
228
	 */
229
	public $wm_padding			= 0;
230
 
231
	/**
232
	 * Lets you push text to the right
233
	 *
234
	 * @var int
235
	 */
236
	public $wm_hor_offset		= 0;
237
 
238
	/**
239
	 * Lets you push text down
240
	 *
241
	 * @var int
242
	 */
243
	public $wm_vrt_offset		= 0;
244
 
245
	/**
246
	 * Text color
247
	 *
248
	 * @var string
249
	 */
250
	protected $wm_font_color	= '#ffffff';
251
 
252
	/**
253
	 * Dropshadow color
254
	 *
255
	 * @var string
256
	 */
257
	protected $wm_shadow_color	= '';
258
 
259
	/**
260
	 * Dropshadow distance
261
	 *
262
	 * @var int
263
	 */
264
	public $wm_shadow_distance	= 2;
265
 
266
	/**
267
	 * Image opacity: 1 - 100  Only works with image
268
	 *
269
	 * @var int
270
	 */
271
	public $wm_opacity		= 50;
272
 
273
	// --------------------------------------------------------------------------
274
	// Private Vars
275
	// --------------------------------------------------------------------------
276
 
277
	/**
278
	 * Source image folder
279
	 *
280
	 * @var string
281
	 */
282
	public $source_folder		= '';
283
 
284
	/**
285
	 * Destination image folder
286
	 *
287
	 * @var string
288
	 */
289
	public $dest_folder		= '';
290
 
291
	/**
292
	 * Image mime-type
293
	 *
294
	 * @var string
295
	 */
296
	public $mime_type		= '';
297
 
298
	/**
299
	 * Original image width
300
	 *
301
	 * @var int
302
	 */
303
	public $orig_width		= '';
304
 
305
	/**
306
	 * Original image height
307
	 *
308
	 * @var int
309
	 */
310
	public $orig_height		= '';
311
 
312
	/**
313
	 * Image format
314
	 *
315
	 * @var string
316
	 */
317
	public $image_type		= '';
318
 
319
	/**
320
	 * Size of current image
321
	 *
322
	 * @var string
323
	 */
324
	public $size_str		= '';
325
 
326
	/**
327
	 * Full path to source image
328
	 *
329
	 * @var string
330
	 */
331
	public $full_src_path		= '';
332
 
333
	/**
334
	 * Full path to destination image
335
	 *
336
	 * @var string
337
	 */
338
	public $full_dst_path		= '';
339
 
340
	/**
341
	 * File permissions
342
	 *
343
	 * @var	int
344
	 */
345
	public $file_permissions = 0644;
346
 
347
	/**
348
	 * Name of function to create image
349
	 *
350
	 * @var string
351
	 */
352
	public $create_fnc		= 'imagecreatetruecolor';
353
 
354
	/**
355
	 * Name of function to copy image
356
	 *
357
	 * @var string
358
	 */
359
	public $copy_fnc		= 'imagecopyresampled';
360
 
361
	/**
362
	 * Error messages
363
	 *
364
	 * @var array
365
	 */
366
	public $error_msg		= array();
367
 
368
	/**
369
	 * Whether to have a drop shadow on watermark
370
	 *
371
	 * @var bool
372
	 */
373
	protected $wm_use_drop_shadow	= FALSE;
374
 
375
	/**
376
	 * Whether to use truetype fonts
377
	 *
378
	 * @var bool
379
	 */
380
	public $wm_use_truetype	= FALSE;
381
 
382
	/**
383
	 * Initialize Image Library
384
	 *
385
	 * @param	array	$props
386
	 * @return	void
387
	 */
388
	public function __construct($props = array())
389
	{
390
		if (count($props) > 0)
391
		{
392
			$this->initialize($props);
393
		}
394
 
2107 lars 395
		/**
396
		 * A work-around for some improperly formatted, but
397
		 * usable JPEGs; known to be produced by Samsung
398
		 * smartphones' front-facing cameras.
399
		 *
400
		 * @see	https://github.com/bcit-ci/CodeIgniter/issues/4967
401
		 * @see	https://bugs.php.net/bug.php?id=72404
402
		 */
403
		ini_set('gd.jpeg_ignore_warning', 1);
404
 
68 lars 405
		log_message('info', 'Image Lib Class Initialized');
406
	}
407
 
408
	// --------------------------------------------------------------------
409
 
410
	/**
411
	 * Initialize image properties
412
	 *
413
	 * Resets values in case this class is used in a loop
414
	 *
415
	 * @return	void
416
	 */
417
	public function clear()
418
	{
419
		$props = array('thumb_marker', 'library_path', 'source_image', 'new_image', 'width', 'height', 'rotation_angle', 'x_axis', 'y_axis', 'wm_text', 'wm_overlay_path', 'wm_font_path', 'wm_shadow_color', 'source_folder', 'dest_folder', 'mime_type', 'orig_width', 'orig_height', 'image_type', 'size_str', 'full_src_path', 'full_dst_path');
420
 
421
		foreach ($props as $val)
422
		{
423
			$this->$val = '';
424
		}
425
 
426
		$this->image_library 		= 'gd2';
427
		$this->dynamic_output 		= FALSE;
428
		$this->quality 				= 90;
429
		$this->create_thumb 		= FALSE;
430
		$this->thumb_marker 		= '_thumb';
431
		$this->maintain_ratio 		= TRUE;
432
		$this->master_dim 			= 'auto';
433
		$this->wm_type 				= 'text';
434
		$this->wm_x_transp 			= 4;
435
		$this->wm_y_transp 			= 4;
436
		$this->wm_font_size 		= 17;
437
		$this->wm_vrt_alignment 	= 'B';
438
		$this->wm_hor_alignment 	= 'C';
439
		$this->wm_padding 			= 0;
440
		$this->wm_hor_offset 		= 0;
441
		$this->wm_vrt_offset 		= 0;
442
		$this->wm_font_color		= '#ffffff';
443
		$this->wm_shadow_distance 	= 2;
444
		$this->wm_opacity 			= 50;
445
		$this->create_fnc 			= 'imagecreatetruecolor';
446
		$this->copy_fnc 			= 'imagecopyresampled';
447
		$this->error_msg 			= array();
448
		$this->wm_use_drop_shadow 	= FALSE;
449
		$this->wm_use_truetype 		= FALSE;
450
	}
451
 
452
	// --------------------------------------------------------------------
453
 
454
	/**
455
	 * initialize image preferences
456
	 *
457
	 * @param	array
458
	 * @return	bool
459
	 */
460
	public function initialize($props = array())
461
	{
462
		// Convert array elements into class variables
463
		if (count($props) > 0)
464
		{
465
			foreach ($props as $key => $val)
466
			{
467
				if (property_exists($this, $key))
468
				{
469
					if (in_array($key, array('wm_font_color', 'wm_shadow_color'), TRUE))
470
					{
471
						if (preg_match('/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i', $val, $matches))
472
						{
473
							/* $matches[1] contains our hex color value, but it might be
474
							 * both in the full 6-length format or the shortened 3-length
475
							 * value.
476
							 * We'll later need the full version, so we keep it if it's
477
							 * already there and if not - we'll convert to it. We can
478
							 * access string characters by their index as in an array,
479
							 * so we'll do that and use concatenation to form the final
480
							 * value:
481
							 */
482
							$val = (strlen($matches[1]) === 6)
483
								? '#'.$matches[1]
484
								: '#'.$matches[1][0].$matches[1][0].$matches[1][1].$matches[1][1].$matches[1][2].$matches[1][2];
485
						}
486
						else
487
						{
488
							continue;
489
						}
490
					}
491
					elseif (in_array($key, array('width', 'height'), TRUE) && ! ctype_digit((string) $val))
492
					{
493
						continue;
494
					}
495
 
496
					$this->$key = $val;
497
				}
498
			}
499
		}
500
 
501
		// Is there a source image? If not, there's no reason to continue
502
		if ($this->source_image === '')
503
		{
504
			$this->set_error('imglib_source_image_required');
505
			return FALSE;
506
		}
507
 
508
		/* Is getimagesize() available?
509
		 *
510
		 * We use it to determine the image properties (width/height).
511
		 * Note: We need to figure out how to determine image
512
		 * properties using ImageMagick and NetPBM
513
		 */
514
		if ( ! function_exists('getimagesize'))
515
		{
516
			$this->set_error('imglib_gd_required_for_props');
517
			return FALSE;
518
		}
519
 
520
		$this->image_library = strtolower($this->image_library);
521
 
522
		/* Set the full server path
523
		 *
524
		 * The source image may or may not contain a path.
525
		 * Either way, we'll try use realpath to generate the
526
		 * full server path in order to more reliably read it.
527
		 */
528
		if (($full_source_path = realpath($this->source_image)) !== FALSE)
529
		{
530
			$full_source_path = str_replace('\\', '/', $full_source_path);
531
		}
532
		else
533
		{
534
			$full_source_path = $this->source_image;
535
		}
536
 
537
		$x = explode('/', $full_source_path);
538
		$this->source_image = end($x);
539
		$this->source_folder = str_replace($this->source_image, '', $full_source_path);
540
 
541
		// Set the Image Properties
542
		if ( ! $this->get_image_properties($this->source_folder.$this->source_image))
543
		{
544
			return FALSE;
545
		}
546
 
547
		/*
548
		 * Assign the "new" image name/path
549
		 *
550
		 * If the user has set a "new_image" name it means
551
		 * we are making a copy of the source image. If not
552
		 * it means we are altering the original. We'll
553
		 * set the destination filename and path accordingly.
554
		 */
555
		if ($this->new_image === '')
556
		{
2049 lars 557
			$this->dest_image  = $this->source_image;
68 lars 558
			$this->dest_folder = $this->source_folder;
559
		}
2049 lars 560
		elseif (strpos($this->new_image, '/') === FALSE && strpos($this->new_image, '\\') === FALSE)
68 lars 561
		{
2049 lars 562
			$this->dest_image  = $this->new_image;
68 lars 563
			$this->dest_folder = $this->source_folder;
564
		}
565
		else
566
		{
2049 lars 567
			// Is there a file name?
568
			if ( ! preg_match('#\.(jpg|jpeg|gif|png)$#i', $this->new_image))
68 lars 569
			{
2049 lars 570
				$this->dest_image  = $this->source_image;
571
				$this->dest_folder = $this->new_image;
68 lars 572
			}
573
			else
574
			{
2049 lars 575
				$x = explode('/', str_replace('\\', '/', $this->new_image));
576
				$this->dest_image  = end($x);
577
				$this->dest_folder = str_replace($this->dest_image, '', $this->new_image);
68 lars 578
			}
579
 
2049 lars 580
			$this->dest_folder = realpath($this->dest_folder).'/';
68 lars 581
		}
582
 
583
		/* Compile the finalized filenames/paths
584
		 *
585
		 * We'll create two master strings containing the
586
		 * full server path to the source image and the
587
		 * full server path to the destination image.
588
		 * We'll also split the destination image name
589
		 * so we can insert the thumbnail marker if needed.
590
		 */
591
		if ($this->create_thumb === FALSE OR $this->thumb_marker === '')
592
		{
593
			$this->thumb_marker = '';
594
		}
595
 
596
		$xp = $this->explode_name($this->dest_image);
597
 
598
		$filename = $xp['name'];
599
		$file_ext = $xp['ext'];
600
 
601
		$this->full_src_path = $this->source_folder.$this->source_image;
602
		$this->full_dst_path = $this->dest_folder.$filename.$this->thumb_marker.$file_ext;
603
 
604
		/* Should we maintain image proportions?
605
		 *
606
		 * When creating thumbs or copies, the target width/height
607
		 * might not be in correct proportion with the source
608
		 * image's width/height. We'll recalculate it here.
609
		 */
610
		if ($this->maintain_ratio === TRUE && ($this->width !== 0 OR $this->height !== 0))
611
		{
612
			$this->image_reproportion();
613
		}
614
 
615
		/* Was a width and height specified?
616
		 *
617
		 * If the destination width/height was not submitted we
618
		 * will use the values from the actual file
619
		 */
620
		if ($this->width === '')
621
		{
622
			$this->width = $this->orig_width;
623
		}
624
 
625
		if ($this->height === '')
626
		{
627
			$this->height = $this->orig_height;
628
		}
629
 
630
		// Set the quality
631
		$this->quality = trim(str_replace('%', '', $this->quality));
632
 
633
		if ($this->quality === '' OR $this->quality === 0 OR ! ctype_digit($this->quality))
634
		{
635
			$this->quality = 90;
636
		}
637
 
638
		// Set the x/y coordinates
639
		is_numeric($this->x_axis) OR $this->x_axis = 0;
640
		is_numeric($this->y_axis) OR $this->y_axis = 0;
641
 
642
		// Watermark-related Stuff...
643
		if ($this->wm_overlay_path !== '')
644
		{
645
			$this->wm_overlay_path = str_replace('\\', '/', realpath($this->wm_overlay_path));
646
		}
647
 
648
		if ($this->wm_shadow_color !== '')
649
		{
650
			$this->wm_use_drop_shadow = TRUE;
651
		}
652
		elseif ($this->wm_use_drop_shadow === TRUE && $this->wm_shadow_color === '')
653
		{
654
			$this->wm_use_drop_shadow = FALSE;
655
		}
656
 
657
		if ($this->wm_font_path !== '')
658
		{
659
			$this->wm_use_truetype = TRUE;
660
		}
661
 
662
		return TRUE;
663
	}
664
 
665
	// --------------------------------------------------------------------
666
 
667
	/**
668
	 * Image Resize
669
	 *
670
	 * This is a wrapper function that chooses the proper
671
	 * resize function based on the protocol specified
672
	 *
673
	 * @return	bool
674
	 */
675
	public function resize()
676
	{
677
		$protocol = ($this->image_library === 'gd2') ? 'image_process_gd' : 'image_process_'.$this->image_library;
678
		return $this->$protocol('resize');
679
	}
680
 
681
	// --------------------------------------------------------------------
682
 
683
	/**
684
	 * Image Crop
685
	 *
686
	 * This is a wrapper function that chooses the proper
687
	 * cropping function based on the protocol specified
688
	 *
689
	 * @return	bool
690
	 */
691
	public function crop()
692
	{
693
		$protocol = ($this->image_library === 'gd2') ? 'image_process_gd' : 'image_process_'.$this->image_library;
694
		return $this->$protocol('crop');
695
	}
696
 
697
	// --------------------------------------------------------------------
698
 
699
	/**
700
	 * Image Rotate
701
	 *
702
	 * This is a wrapper function that chooses the proper
703
	 * rotation function based on the protocol specified
704
	 *
705
	 * @return	bool
706
	 */
707
	public function rotate()
708
	{
709
		// Allowed rotation values
710
		$degs = array(90, 180, 270, 'vrt', 'hor');
711
 
712
		if ($this->rotation_angle === '' OR ! in_array($this->rotation_angle, $degs))
713
		{
714
			$this->set_error('imglib_rotation_angle_required');
715
			return FALSE;
716
		}
717
 
718
		// Reassign the width and height
719
		if ($this->rotation_angle === 90 OR $this->rotation_angle === 270)
720
		{
721
			$this->width	= $this->orig_height;
722
			$this->height	= $this->orig_width;
723
		}
724
		else
725
		{
726
			$this->width	= $this->orig_width;
727
			$this->height	= $this->orig_height;
728
		}
729
 
730
		// Choose resizing function
731
		if ($this->image_library === 'imagemagick' OR $this->image_library === 'netpbm')
732
		{
733
			$protocol = 'image_process_'.$this->image_library;
734
			return $this->$protocol('rotate');
735
		}
736
 
737
		return ($this->rotation_angle === 'hor' OR $this->rotation_angle === 'vrt')
738
			? $this->image_mirror_gd()
739
			: $this->image_rotate_gd();
740
	}
741
 
742
	// --------------------------------------------------------------------
743
 
744
	/**
745
	 * Image Process Using GD/GD2
746
	 *
747
	 * This function will resize or crop
748
	 *
749
	 * @param	string
750
	 * @return	bool
751
	 */
752
	public function image_process_gd($action = 'resize')
753
	{
754
		$v2_override = FALSE;
755
 
756
		// If the target width/height match the source, AND if the new file name is not equal to the old file name
757
		// we'll simply make a copy of the original with the new name... assuming dynamic rendering is off.
758
		if ($this->dynamic_output === FALSE && $this->orig_width === $this->width && $this->orig_height === $this->height)
759
		{
760
			if ($this->source_image !== $this->new_image && @copy($this->full_src_path, $this->full_dst_path))
761
			{
762
				chmod($this->full_dst_path, $this->file_permissions);
763
			}
764
 
765
			return TRUE;
766
		}
767
 
768
		// Let's set up our values based on the action
769
		if ($action === 'crop')
770
		{
771
			// Reassign the source width/height if cropping
772
			$this->orig_width  = $this->width;
773
			$this->orig_height = $this->height;
774
 
775
			// GD 2.0 has a cropping bug so we'll test for it
776
			if ($this->gd_version() !== FALSE)
777
			{
778
				$gd_version = str_replace('0', '', $this->gd_version());
779
				$v2_override = ($gd_version == 2);
780
			}
781
		}
782
		else
783
		{
784
			// If resizing the x/y axis must be zero
785
			$this->x_axis = 0;
786
			$this->y_axis = 0;
787
		}
788
 
789
		// Create the image handle
790
		if ( ! ($src_img = $this->image_create_gd()))
791
		{
792
			return FALSE;
793
		}
794
 
795
		/* Create the image
796
		 *
797
		 * Old conditional which users report cause problems with shared GD libs who report themselves as "2.0 or greater"
798
		 * it appears that this is no longer the issue that it was in 2004, so we've removed it, retaining it in the comment
799
		 * below should that ever prove inaccurate.
800
		 *
801
		 * if ($this->image_library === 'gd2' && function_exists('imagecreatetruecolor') && $v2_override === FALSE)
802
		 */
803
		if ($this->image_library === 'gd2' && function_exists('imagecreatetruecolor'))
804
		{
805
			$create	= 'imagecreatetruecolor';
806
			$copy	= 'imagecopyresampled';
807
		}
808
		else
809
		{
810
			$create	= 'imagecreate';
811
			$copy	= 'imagecopyresized';
812
		}
813
 
814
		$dst_img = $create($this->width, $this->height);
815
 
816
		if ($this->image_type === 3) // png we can actually preserve transparency
817
		{
818
			imagealphablending($dst_img, FALSE);
819
			imagesavealpha($dst_img, TRUE);
820
		}
821
 
822
		$copy($dst_img, $src_img, 0, 0, $this->x_axis, $this->y_axis, $this->width, $this->height, $this->orig_width, $this->orig_height);
823
 
824
		// Show the image
825
		if ($this->dynamic_output === TRUE)
826
		{
827
			$this->image_display_gd($dst_img);
828
		}
829
		elseif ( ! $this->image_save_gd($dst_img)) // Or save it
830
		{
831
			return FALSE;
832
		}
833
 
834
		// Kill the file handles
835
		imagedestroy($dst_img);
836
		imagedestroy($src_img);
837
 
838
		chmod($this->full_dst_path, $this->file_permissions);
839
 
840
		return TRUE;
841
	}
842
 
843
	// --------------------------------------------------------------------
844
 
845
	/**
846
	 * Image Process Using ImageMagick
847
	 *
848
	 * This function will resize, crop or rotate
849
	 *
850
	 * @param	string
851
	 * @return	bool
852
	 */
853
	public function image_process_imagemagick($action = 'resize')
854
	{
855
		// Do we have a vaild library path?
856
		if ($this->library_path === '')
857
		{
858
			$this->set_error('imglib_libpath_invalid');
859
			return FALSE;
860
		}
861
 
862
		if ( ! preg_match('/convert$/i', $this->library_path))
863
		{
864
			$this->library_path = rtrim($this->library_path, '/').'/convert';
865
		}
866
 
867
		// Execute the command
868
		$cmd = $this->library_path.' -quality '.$this->quality;
869
 
870
		if ($action === 'crop')
871
		{
872
			$cmd .= ' -crop '.$this->width.'x'.$this->height.'+'.$this->x_axis.'+'.$this->y_axis;
873
		}
874
		elseif ($action === 'rotate')
875
		{
876
			$cmd .= ($this->rotation_angle === 'hor' OR $this->rotation_angle === 'vrt')
877
					? ' -flop'
878
					: ' -rotate '.$this->rotation_angle;
879
		}
880
		else // Resize
881
		{
882
			if($this->maintain_ratio === TRUE)
883
			{
884
				$cmd .= ' -resize '.$this->width.'x'.$this->height;
885
			}
886
			else
887
			{
888
				$cmd .= ' -resize '.$this->width.'x'.$this->height.'\!';
889
			}
890
		}
891
 
2049 lars 892
		$cmd .= ' '.escapeshellarg($this->full_src_path).' '.escapeshellarg($this->full_dst_path).' 2>&1';
68 lars 893
 
894
		$retval = 1;
895
		// exec() might be disabled
896
		if (function_usable('exec'))
897
		{
898
			@exec($cmd, $output, $retval);
899
		}
900
 
901
		// Did it work?
902
		if ($retval > 0)
903
		{
904
			$this->set_error('imglib_image_process_failed');
905
			return FALSE;
906
		}
907
 
908
		chmod($this->full_dst_path, $this->file_permissions);
909
 
910
		return TRUE;
911
	}
912
 
913
	// --------------------------------------------------------------------
914
 
915
	/**
916
	 * Image Process Using NetPBM
917
	 *
918
	 * This function will resize, crop or rotate
919
	 *
920
	 * @param	string
921
	 * @return	bool
922
	 */
923
	public function image_process_netpbm($action = 'resize')
924
	{
925
		if ($this->library_path === '')
926
		{
927
			$this->set_error('imglib_libpath_invalid');
928
			return FALSE;
929
		}
930
 
931
		// Build the resizing command
932
		switch ($this->image_type)
933
		{
934
			case 1 :
935
				$cmd_in		= 'giftopnm';
936
				$cmd_out	= 'ppmtogif';
937
				break;
938
			case 2 :
939
				$cmd_in		= 'jpegtopnm';
940
				$cmd_out	= 'ppmtojpeg';
941
				break;
942
			case 3 :
943
				$cmd_in		= 'pngtopnm';
944
				$cmd_out	= 'ppmtopng';
945
				break;
946
		}
947
 
948
		if ($action === 'crop')
949
		{
950
			$cmd_inner = 'pnmcut -left '.$this->x_axis.' -top '.$this->y_axis.' -width '.$this->width.' -height '.$this->height;
951
		}
952
		elseif ($action === 'rotate')
953
		{
954
			switch ($this->rotation_angle)
955
			{
956
				case 90:	$angle = 'r270';
957
					break;
958
				case 180:	$angle = 'r180';
959
					break;
960
				case 270:	$angle = 'r90';
961
					break;
962
				case 'vrt':	$angle = 'tb';
963
					break;
964
				case 'hor':	$angle = 'lr';
965
					break;
966
			}
967
 
968
			$cmd_inner = 'pnmflip -'.$angle.' ';
969
		}
970
		else // Resize
971
		{
972
			$cmd_inner = 'pnmscale -xysize '.$this->width.' '.$this->height;
973
		}
974
 
2107 lars 975
		$cmd = $this->library_path.$cmd_in.' '.escapeshellarg($this->full_src_path).' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp';
68 lars 976
 
977
		$retval = 1;
978
		// exec() might be disabled
979
		if (function_usable('exec'))
980
		{
981
			@exec($cmd, $output, $retval);
982
		}
983
 
984
		// Did it work?
985
		if ($retval > 0)
986
		{
987
			$this->set_error('imglib_image_process_failed');
988
			return FALSE;
989
		}
990
 
991
		// With NetPBM we have to create a temporary image.
992
		// If you try manipulating the original it fails so
993
		// we have to rename the temp file.
994
		copy($this->dest_folder.'netpbm.tmp', $this->full_dst_path);
995
		unlink($this->dest_folder.'netpbm.tmp');
996
		chmod($this->full_dst_path, $this->file_permissions);
997
 
998
		return TRUE;
999
	}
1000
 
1001
	// --------------------------------------------------------------------
1002
 
1003
	/**
1004
	 * Image Rotate Using GD
1005
	 *
1006
	 * @return	bool
1007
	 */
1008
	public function image_rotate_gd()
1009
	{
1010
		// Create the image handle
1011
		if ( ! ($src_img = $this->image_create_gd()))
1012
		{
1013
			return FALSE;
1014
		}
1015
 
1016
		// Set the background color
1017
		// This won't work with transparent PNG files so we are
1018
		// going to have to figure out how to determine the color
1019
		// of the alpha channel in a future release.
1020
 
1021
		$white = imagecolorallocate($src_img, 255, 255, 255);
1022
 
1023
		// Rotate it!
1024
		$dst_img = imagerotate($src_img, $this->rotation_angle, $white);
1025
 
1026
		// Show the image
1027
		if ($this->dynamic_output === TRUE)
1028
		{
1029
			$this->image_display_gd($dst_img);
1030
		}
1031
		elseif ( ! $this->image_save_gd($dst_img)) // ... or save it
1032
		{
1033
			return FALSE;
1034
		}
1035
 
1036
		// Kill the file handles
1037
		imagedestroy($dst_img);
1038
		imagedestroy($src_img);
1039
 
1040
		chmod($this->full_dst_path, $this->file_permissions);
1041
 
1042
		return TRUE;
1043
	}
1044
 
1045
	// --------------------------------------------------------------------
1046
 
1047
	/**
1048
	 * Create Mirror Image using GD
1049
	 *
1050
	 * This function will flip horizontal or vertical
1051
	 *
1052
	 * @return	bool
1053
	 */
1054
	public function image_mirror_gd()
1055
	{
1056
		if ( ! $src_img = $this->image_create_gd())
1057
		{
1058
			return FALSE;
1059
		}
1060
 
1061
		$width  = $this->orig_width;
1062
		$height = $this->orig_height;
1063
 
1064
		if ($this->rotation_angle === 'hor')
1065
		{
1066
			for ($i = 0; $i < $height; $i++)
1067
			{
1068
				$left = 0;
1069
				$right = $width - 1;
1070
 
1071
				while ($left < $right)
1072
				{
1073
					$cl = imagecolorat($src_img, $left, $i);
1074
					$cr = imagecolorat($src_img, $right, $i);
1075
 
1076
					imagesetpixel($src_img, $left, $i, $cr);
1077
					imagesetpixel($src_img, $right, $i, $cl);
1078
 
1079
					$left++;
1080
					$right--;
1081
				}
1082
			}
1083
		}
1084
		else
1085
		{
1086
			for ($i = 0; $i < $width; $i++)
1087
			{
1088
				$top = 0;
1089
				$bottom = $height - 1;
1090
 
1091
				while ($top < $bottom)
1092
				{
1093
					$ct = imagecolorat($src_img, $i, $top);
1094
					$cb = imagecolorat($src_img, $i, $bottom);
1095
 
1096
					imagesetpixel($src_img, $i, $top, $cb);
1097
					imagesetpixel($src_img, $i, $bottom, $ct);
1098
 
1099
					$top++;
1100
					$bottom--;
1101
				}
1102
			}
1103
		}
1104
 
1105
		// Show the image
1106
		if ($this->dynamic_output === TRUE)
1107
		{
1108
			$this->image_display_gd($src_img);
1109
		}
1110
		elseif ( ! $this->image_save_gd($src_img)) // ... or save it
1111
		{
1112
			return FALSE;
1113
		}
1114
 
1115
		// Kill the file handles
1116
		imagedestroy($src_img);
1117
 
1118
		chmod($this->full_dst_path, $this->file_permissions);
1119
 
1120
		return TRUE;
1121
	}
1122
 
1123
	// --------------------------------------------------------------------
1124
 
1125
	/**
1126
	 * Image Watermark
1127
	 *
1128
	 * This is a wrapper function that chooses the type
1129
	 * of watermarking based on the specified preference.
1130
	 *
1131
	 * @return	bool
1132
	 */
1133
	public function watermark()
1134
	{
1135
		return ($this->wm_type === 'overlay') ? $this->overlay_watermark() : $this->text_watermark();
1136
	}
1137
 
1138
	// --------------------------------------------------------------------
1139
 
1140
	/**
1141
	 * Watermark - Graphic Version
1142
	 *
1143
	 * @return	bool
1144
	 */
1145
	public function overlay_watermark()
1146
	{
1147
		if ( ! function_exists('imagecolortransparent'))
1148
		{
1149
			$this->set_error('imglib_gd_required');
1150
			return FALSE;
1151
		}
1152
 
1153
		// Fetch source image properties
1154
		$this->get_image_properties();
1155
 
1156
		// Fetch watermark image properties
1157
		$props		= $this->get_image_properties($this->wm_overlay_path, TRUE);
1158
		$wm_img_type	= $props['image_type'];
1159
		$wm_width	= $props['width'];
1160
		$wm_height	= $props['height'];
1161
 
1162
		// Create two image resources
1163
		$wm_img  = $this->image_create_gd($this->wm_overlay_path, $wm_img_type);
1164
		$src_img = $this->image_create_gd($this->full_src_path);
1165
 
1166
		// Reverse the offset if necessary
1167
		// When the image is positioned at the bottom
1168
		// we don't want the vertical offset to push it
1169
		// further down. We want the reverse, so we'll
1170
		// invert the offset. Same with the horizontal
1171
		// offset when the image is at the right
1172
 
1173
		$this->wm_vrt_alignment = strtoupper($this->wm_vrt_alignment[0]);
1174
		$this->wm_hor_alignment = strtoupper($this->wm_hor_alignment[0]);
1175
 
1176
		if ($this->wm_vrt_alignment === 'B')
1177
			$this->wm_vrt_offset = $this->wm_vrt_offset * -1;
1178
 
1179
		if ($this->wm_hor_alignment === 'R')
1180
			$this->wm_hor_offset = $this->wm_hor_offset * -1;
1181
 
1182
		// Set the base x and y axis values
1183
		$x_axis = $this->wm_hor_offset + $this->wm_padding;
1184
		$y_axis = $this->wm_vrt_offset + $this->wm_padding;
1185
 
1186
		// Set the vertical position
1187
		if ($this->wm_vrt_alignment === 'M')
1188
		{
1189
			$y_axis += ($this->orig_height / 2) - ($wm_height / 2);
1190
		}
1191
		elseif ($this->wm_vrt_alignment === 'B')
1192
		{
1193
			$y_axis += $this->orig_height - $wm_height;
1194
		}
1195
 
1196
		// Set the horizontal position
1197
		if ($this->wm_hor_alignment === 'C')
1198
		{
1199
			$x_axis += ($this->orig_width / 2) - ($wm_width / 2);
1200
		}
1201
		elseif ($this->wm_hor_alignment === 'R')
1202
		{
1203
			$x_axis += $this->orig_width - $wm_width;
1204
		}
1205
 
1206
		// Build the finalized image
1207
		if ($wm_img_type === 3 && function_exists('imagealphablending'))
1208
		{
1209
			@imagealphablending($src_img, TRUE);
1210
		}
1211
 
1212
		// Set RGB values for text and shadow
1213
		$rgba = imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp);
1214
		$alpha = ($rgba & 0x7F000000) >> 24;
1215
 
1216
		// make a best guess as to whether we're dealing with an image with alpha transparency or no/binary transparency
1217
		if ($alpha > 0)
1218
		{
1219
			// copy the image directly, the image's alpha transparency being the sole determinant of blending
1220
			imagecopy($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height);
1221
		}
1222
		else
1223
		{
1224
			// set our RGB value from above to be transparent and merge the images with the specified opacity
1225
			imagecolortransparent($wm_img, imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp));
1226
			imagecopymerge($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height, $this->wm_opacity);
1227
		}
1228
 
1229
		// We can preserve transparency for PNG images
1230
		if ($this->image_type === 3)
1231
		{
1232
			imagealphablending($src_img, FALSE);
1233
			imagesavealpha($src_img, TRUE);
1234
		}
1235
 
1236
		// Output the image
1237
		if ($this->dynamic_output === TRUE)
1238
		{
1239
			$this->image_display_gd($src_img);
1240
		}
1241
		elseif ( ! $this->image_save_gd($src_img)) // ... or save it
1242
		{
1243
			return FALSE;
1244
		}
1245
 
1246
		imagedestroy($src_img);
1247
		imagedestroy($wm_img);
1248
 
1249
		return TRUE;
1250
	}
1251
 
1252
	// --------------------------------------------------------------------
1253
 
1254
	/**
1255
	 * Watermark - Text Version
1256
	 *
1257
	 * @return	bool
1258
	 */
1259
	public function text_watermark()
1260
	{
1261
		if ( ! ($src_img = $this->image_create_gd()))
1262
		{
1263
			return FALSE;
1264
		}
1265
 
1266
		if ($this->wm_use_truetype === TRUE && ! file_exists($this->wm_font_path))
1267
		{
1268
			$this->set_error('imglib_missing_font');
1269
			return FALSE;
1270
		}
1271
 
1272
		// Fetch source image properties
1273
		$this->get_image_properties();
1274
 
1275
		// Reverse the vertical offset
1276
		// When the image is positioned at the bottom
1277
		// we don't want the vertical offset to push it
1278
		// further down. We want the reverse, so we'll
1279
		// invert the offset. Note: The horizontal
1280
		// offset flips itself automatically
1281
 
1282
		if ($this->wm_vrt_alignment === 'B')
1283
		{
1284
			$this->wm_vrt_offset = $this->wm_vrt_offset * -1;
1285
		}
1286
 
1287
		if ($this->wm_hor_alignment === 'R')
1288
		{
1289
			$this->wm_hor_offset = $this->wm_hor_offset * -1;
1290
		}
1291
 
1292
		// Set font width and height
1293
		// These are calculated differently depending on
1294
		// whether we are using the true type font or not
1295
		if ($this->wm_use_truetype === TRUE)
1296
		{
1297
			if (empty($this->wm_font_size))
1298
			{
1299
				$this->wm_font_size = 17;
1300
			}
1301
 
1302
			if (function_exists('imagettfbbox'))
1303
			{
1304
				$temp = imagettfbbox($this->wm_font_size, 0, $this->wm_font_path, $this->wm_text);
1305
				$temp = $temp[2] - $temp[0];
1306
 
1307
				$fontwidth = $temp / strlen($this->wm_text);
1308
			}
1309
			else
1310
			{
1311
				$fontwidth = $this->wm_font_size - ($this->wm_font_size / 4);
1312
			}
1313
 
1314
			$fontheight = $this->wm_font_size;
1315
			$this->wm_vrt_offset += $this->wm_font_size;
1316
		}
1317
		else
1318
		{
1319
			$fontwidth  = imagefontwidth($this->wm_font_size);
1320
			$fontheight = imagefontheight($this->wm_font_size);
1321
		}
1322
 
1323
		// Set base X and Y axis values
1324
		$x_axis = $this->wm_hor_offset + $this->wm_padding;
1325
		$y_axis = $this->wm_vrt_offset + $this->wm_padding;
1326
 
1327
		if ($this->wm_use_drop_shadow === FALSE)
1328
		{
1329
			$this->wm_shadow_distance = 0;
1330
		}
1331
 
1332
		$this->wm_vrt_alignment = strtoupper($this->wm_vrt_alignment[0]);
1333
		$this->wm_hor_alignment = strtoupper($this->wm_hor_alignment[0]);
1334
 
1335
		// Set vertical alignment
1336
		if ($this->wm_vrt_alignment === 'M')
1337
		{
1338
			$y_axis += ($this->orig_height / 2) + ($fontheight / 2);
1339
		}
1340
		elseif ($this->wm_vrt_alignment === 'B')
1341
		{
1342
			$y_axis += $this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight / 2);
1343
		}
1344
 
1345
		// Set horizontal alignment
1346
		if ($this->wm_hor_alignment === 'R')
1347
		{
1348
			$x_axis += $this->orig_width - ($fontwidth * strlen($this->wm_text)) - $this->wm_shadow_distance;
1349
		}
1350
		elseif ($this->wm_hor_alignment === 'C')
1351
		{
1352
			$x_axis += floor(($this->orig_width - ($fontwidth * strlen($this->wm_text))) / 2);
1353
		}
1354
 
1355
		if ($this->wm_use_drop_shadow)
1356
		{
1357
			// Offset from text
1358
			$x_shad = $x_axis + $this->wm_shadow_distance;
1359
			$y_shad = $y_axis + $this->wm_shadow_distance;
1360
 
1361
			/* Set RGB values for shadow
1362
			 *
1363
			 * First character is #, so we don't really need it.
1364
			 * Get the rest of the string and split it into 2-length
1365
			 * hex values:
1366
			 */
1367
			$drp_color = str_split(substr($this->wm_shadow_color, 1, 6), 2);
1368
			$drp_color = imagecolorclosest($src_img, hexdec($drp_color[0]), hexdec($drp_color[1]), hexdec($drp_color[2]));
1369
 
1370
			// Add the shadow to the source image
1371
			if ($this->wm_use_truetype)
1372
			{
1373
				imagettftext($src_img, $this->wm_font_size, 0, $x_shad, $y_shad, $drp_color, $this->wm_font_path, $this->wm_text);
1374
			}
1375
			else
1376
			{
1377
				imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color);
1378
			}
1379
		}
1380
 
1381
		/* Set RGB values for text
1382
		 *
1383
		 * First character is #, so we don't really need it.
1384
		 * Get the rest of the string and split it into 2-length
1385
		 * hex values:
1386
		 */
1387
		$txt_color = str_split(substr($this->wm_font_color, 1, 6), 2);
1388
		$txt_color = imagecolorclosest($src_img, hexdec($txt_color[0]), hexdec($txt_color[1]), hexdec($txt_color[2]));
1389
 
1390
		// Add the text to the source image
1391
		if ($this->wm_use_truetype)
1392
		{
1393
			imagettftext($src_img, $this->wm_font_size, 0, $x_axis, $y_axis, $txt_color, $this->wm_font_path, $this->wm_text);
1394
		}
1395
		else
1396
		{
1397
			imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color);
1398
		}
1399
 
1400
		// We can preserve transparency for PNG images
1401
		if ($this->image_type === 3)
1402
		{
1403
			imagealphablending($src_img, FALSE);
1404
			imagesavealpha($src_img, TRUE);
1405
		}
1406
 
1407
		// Output the final image
1408
		if ($this->dynamic_output === TRUE)
1409
		{
1410
			$this->image_display_gd($src_img);
1411
		}
1412
		else
1413
		{
1414
			$this->image_save_gd($src_img);
1415
		}
1416
 
1417
		imagedestroy($src_img);
1418
 
1419
		return TRUE;
1420
	}
1421
 
1422
	// --------------------------------------------------------------------
1423
 
1424
	/**
1425
	 * Create Image - GD
1426
	 *
1427
	 * This simply creates an image resource handle
1428
	 * based on the type of image being processed
1429
	 *
1430
	 * @param	string
1431
	 * @param	string
1432
	 * @return	resource
1433
	 */
1434
	public function image_create_gd($path = '', $image_type = '')
1435
	{
1436
		if ($path === '')
1437
		{
1438
			$path = $this->full_src_path;
1439
		}
1440
 
1441
		if ($image_type === '')
1442
		{
1443
			$image_type = $this->image_type;
1444
		}
1445
 
1446
		switch ($image_type)
1447
		{
1448
			case 1:
1449
				if ( ! function_exists('imagecreatefromgif'))
1450
				{
1451
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
1452
					return FALSE;
1453
				}
1454
 
1455
				return imagecreatefromgif($path);
1456
			case 2:
1457
				if ( ! function_exists('imagecreatefromjpeg'))
1458
				{
1459
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
1460
					return FALSE;
1461
				}
1462
 
1463
				return imagecreatefromjpeg($path);
1464
			case 3:
1465
				if ( ! function_exists('imagecreatefrompng'))
1466
				{
1467
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
1468
					return FALSE;
1469
				}
1470
 
1471
				return imagecreatefrompng($path);
1472
			default:
1473
				$this->set_error(array('imglib_unsupported_imagecreate'));
1474
				return FALSE;
1475
		}
1476
	}
1477
 
1478
	// --------------------------------------------------------------------
1479
 
1480
	/**
1481
	 * Write image file to disk - GD
1482
	 *
1483
	 * Takes an image resource as input and writes the file
1484
	 * to the specified destination
1485
	 *
1486
	 * @param	resource
1487
	 * @return	bool
1488
	 */
1489
	public function image_save_gd($resource)
1490
	{
1491
		switch ($this->image_type)
1492
		{
1493
			case 1:
1494
				if ( ! function_exists('imagegif'))
1495
				{
1496
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
1497
					return FALSE;
1498
				}
1499
 
1500
				if ( ! @imagegif($resource, $this->full_dst_path))
1501
				{
1502
					$this->set_error('imglib_save_failed');
1503
					return FALSE;
1504
				}
1505
			break;
1506
			case 2:
1507
				if ( ! function_exists('imagejpeg'))
1508
				{
1509
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
1510
					return FALSE;
1511
				}
1512
 
1513
				if ( ! @imagejpeg($resource, $this->full_dst_path, $this->quality))
1514
				{
1515
					$this->set_error('imglib_save_failed');
1516
					return FALSE;
1517
				}
1518
			break;
1519
			case 3:
1520
				if ( ! function_exists('imagepng'))
1521
				{
1522
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
1523
					return FALSE;
1524
				}
1525
 
1526
				if ( ! @imagepng($resource, $this->full_dst_path))
1527
				{
1528
					$this->set_error('imglib_save_failed');
1529
					return FALSE;
1530
				}
1531
			break;
1532
			default:
1533
				$this->set_error(array('imglib_unsupported_imagecreate'));
1534
				return FALSE;
1535
			break;
1536
		}
1537
 
1538
		return TRUE;
1539
	}
1540
 
1541
	// --------------------------------------------------------------------
1542
 
1543
	/**
1544
	 * Dynamically outputs an image
1545
	 *
1546
	 * @param	resource
1547
	 * @return	void
1548
	 */
1549
	public function image_display_gd($resource)
1550
	{
1551
		header('Content-Disposition: filename='.$this->source_image.';');
1552
		header('Content-Type: '.$this->mime_type);
1553
		header('Content-Transfer-Encoding: binary');
1554
		header('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT');
1555
 
1556
		switch ($this->image_type)
1557
		{
1558
			case 1	:	imagegif($resource);
1559
				break;
1560
			case 2	:	imagejpeg($resource, NULL, $this->quality);
1561
				break;
1562
			case 3	:	imagepng($resource);
1563
				break;
1564
			default:	echo 'Unable to display the image';
1565
				break;
1566
		}
1567
	}
1568
 
1569
	// --------------------------------------------------------------------
1570
 
1571
	/**
1572
	 * Re-proportion Image Width/Height
1573
	 *
1574
	 * When creating thumbs, the desired width/height
1575
	 * can end up warping the image due to an incorrect
1576
	 * ratio between the full-sized image and the thumb.
1577
	 *
1578
	 * This function lets us re-proportion the width/height
1579
	 * if users choose to maintain the aspect ratio when resizing.
1580
	 *
1581
	 * @return	void
1582
	 */
1583
	public function image_reproportion()
1584
	{
1585
		if (($this->width === 0 && $this->height === 0) OR $this->orig_width === 0 OR $this->orig_height === 0
1586
			OR ( ! ctype_digit((string) $this->width) && ! ctype_digit((string) $this->height))
1587
			OR ! ctype_digit((string) $this->orig_width) OR ! ctype_digit((string) $this->orig_height))
1588
		{
1589
			return;
1590
		}
1591
 
1592
		// Sanitize
1593
		$this->width = (int) $this->width;
1594
		$this->height = (int) $this->height;
1595
 
1596
		if ($this->master_dim !== 'width' && $this->master_dim !== 'height')
1597
		{
1598
			if ($this->width > 0 && $this->height > 0)
1599
			{
1600
				$this->master_dim = ((($this->orig_height/$this->orig_width) - ($this->height/$this->width)) < 0)
1601
							? 'width' : 'height';
1602
			}
1603
			else
1604
			{
1605
				$this->master_dim = ($this->height === 0) ? 'width' : 'height';
1606
			}
1607
		}
1608
		elseif (($this->master_dim === 'width' && $this->width === 0)
1609
			OR ($this->master_dim === 'height' && $this->height === 0))
1610
		{
1611
			return;
1612
		}
1613
 
1614
		if ($this->master_dim === 'width')
1615
		{
1616
			$this->height = (int) ceil($this->width*$this->orig_height/$this->orig_width);
1617
		}
1618
		else
1619
		{
1620
			$this->width = (int) ceil($this->orig_width*$this->height/$this->orig_height);
1621
		}
1622
	}
1623
 
1624
	// --------------------------------------------------------------------
1625
 
1626
	/**
1627
	 * Get image properties
1628
	 *
1629
	 * A helper function that gets info about the file
1630
	 *
1631
	 * @param	string
1632
	 * @param	bool
1633
	 * @return	mixed
1634
	 */
1635
	public function get_image_properties($path = '', $return = FALSE)
1636
	{
1637
		// For now we require GD but we should
1638
		// find a way to determine this using IM or NetPBM
1639
 
1640
		if ($path === '')
1641
		{
1642
			$path = $this->full_src_path;
1643
		}
1644
 
1645
		if ( ! file_exists($path))
1646
		{
1647
			$this->set_error('imglib_invalid_path');
1648
			return FALSE;
1649
		}
1650
 
1651
		$vals = getimagesize($path);
2049 lars 1652
		if ($vals === FALSE)
1653
		{
1654
			$this->set_error('imglib_invalid_image');
1655
			return FALSE;
1656
		}
1657
 
68 lars 1658
		$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
2049 lars 1659
		$mime = isset($types[$vals[2]]) ? 'image/'.$types[$vals[2]] : 'image/jpg';
68 lars 1660
 
1661
		if ($return === TRUE)
1662
		{
1663
			return array(
2049 lars 1664
				'width'      => $vals[0],
1665
				'height'     => $vals[1],
1666
				'image_type' => $vals[2],
1667
				'size_str'   => $vals[3],
1668
				'mime_type'  => $mime
1669
			);
68 lars 1670
		}
1671
 
2049 lars 1672
		$this->orig_width  = $vals[0];
1673
		$this->orig_height = $vals[1];
1674
		$this->image_type  = $vals[2];
1675
		$this->size_str    = $vals[3];
1676
		$this->mime_type   = $mime;
68 lars 1677
 
1678
		return TRUE;
1679
	}
1680
 
1681
	// --------------------------------------------------------------------
1682
 
1683
	/**
1684
	 * Size calculator
1685
	 *
1686
	 * This function takes a known width x height and
1687
	 * recalculates it to a new size. Only one
1688
	 * new variable needs to be known
1689
	 *
1690
	 *	$props = array(
1691
	 *			'width'		=> $width,
1692
	 *			'height'	=> $height,
1693
	 *			'new_width'	=> 40,
1694
	 *			'new_height'	=> ''
1695
	 *		);
1696
	 *
1697
	 * @param	array
1698
	 * @return	array
1699
	 */
1700
	public function size_calculator($vals)
1701
	{
1702
		if ( ! is_array($vals))
1703
		{
1704
			return;
1705
		}
1706
 
1707
		$allowed = array('new_width', 'new_height', 'width', 'height');
1708
 
1709
		foreach ($allowed as $item)
1710
		{
1711
			if (empty($vals[$item]))
1712
			{
1713
				$vals[$item] = 0;
1714
			}
1715
		}
1716
 
1717
		if ($vals['width'] === 0 OR $vals['height'] === 0)
1718
		{
1719
			return $vals;
1720
		}
1721
 
1722
		if ($vals['new_width'] === 0)
1723
		{
1724
			$vals['new_width'] = ceil($vals['width']*$vals['new_height']/$vals['height']);
1725
		}
1726
		elseif ($vals['new_height'] === 0)
1727
		{
1728
			$vals['new_height'] = ceil($vals['new_width']*$vals['height']/$vals['width']);
1729
		}
1730
 
1731
		return $vals;
1732
	}
1733
 
1734
	// --------------------------------------------------------------------
1735
 
1736
	/**
1737
	 * Explode source_image
1738
	 *
1739
	 * This is a helper function that extracts the extension
1740
	 * from the source_image.  This function lets us deal with
1741
	 * source_images with multiple periods, like: my.cool.jpg
1742
	 * It returns an associative array with two elements:
1743
	 * $array['ext']  = '.jpg';
1744
	 * $array['name'] = 'my.cool';
1745
	 *
1746
	 * @param	array
1747
	 * @return	array
1748
	 */
1749
	public function explode_name($source_image)
1750
	{
1751
		$ext = strrchr($source_image, '.');
1752
		$name = ($ext === FALSE) ? $source_image : substr($source_image, 0, -strlen($ext));
1753
 
1754
		return array('ext' => $ext, 'name' => $name);
1755
	}
1756
 
1757
	// --------------------------------------------------------------------
1758
 
1759
	/**
1760
	 * Is GD Installed?
1761
	 *
1762
	 * @return	bool
1763
	 */
1764
	public function gd_loaded()
1765
	{
1766
		if ( ! extension_loaded('gd'))
1767
		{
1768
			/* As it is stated in the PHP manual, dl() is not always available
1769
			 * and even if so - it could generate an E_WARNING message on failure
1770
			 */
1771
			return (function_exists('dl') && @dl('gd.so'));
1772
		}
1773
 
1774
		return TRUE;
1775
	}
1776
 
1777
	// --------------------------------------------------------------------
1778
 
1779
	/**
1780
	 * Get GD version
1781
	 *
1782
	 * @return	mixed
1783
	 */
1784
	public function gd_version()
1785
	{
1786
		if (function_exists('gd_info'))
1787
		{
1788
			$gd_version = @gd_info();
1789
			return preg_replace('/\D/', '', $gd_version['GD Version']);
1790
		}
1791
 
1792
		return FALSE;
1793
	}
1794
 
1795
	// --------------------------------------------------------------------
1796
 
1797
	/**
1798
	 * Set error message
1799
	 *
1800
	 * @param	string
1801
	 * @return	void
1802
	 */
1803
	public function set_error($msg)
1804
	{
1805
		$CI =& get_instance();
1806
		$CI->lang->load('imglib');
1807
 
1808
		if (is_array($msg))
1809
		{
1810
			foreach ($msg as $val)
1811
			{
1812
				$msg = ($CI->lang->line($val) === FALSE) ? $val : $CI->lang->line($val);
1813
				$this->error_msg[] = $msg;
1814
				log_message('error', $msg);
1815
			}
1816
		}
1817
		else
1818
		{
1819
			$msg = ($CI->lang->line($msg) === FALSE) ? $msg : $CI->lang->line($msg);
1820
			$this->error_msg[] = $msg;
1821
			log_message('error', $msg);
1822
		}
1823
	}
1824
 
1825
	// --------------------------------------------------------------------
1826
 
1827
	/**
1828
	 * Show error messages
1829
	 *
1830
	 * @param	string
1831
	 * @param	string
1832
	 * @return	string
1833
	 */
1834
	public function display_errors($open = '<p>', $close = '</p>')
1835
	{
1836
		return (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : '';
1837
	}
1838
 
1839
}