Subversion-Projekte lars-tiefland.ci

Revision

Revision 2257 | 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
 *
2414 lars 9
 * Copyright (c) 2014 - 2019, 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/)
2414 lars 32
 * @copyright	Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
33
 * @license	https://opensource.org/licenses/MIT	MIT License
68 lars 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
 
2257 lars 838
		if ($this->dynamic_output !== TRUE)
839
		{
840
			chmod($this->full_dst_path, $this->file_permissions);
841
		}
68 lars 842
 
843
		return TRUE;
844
	}
845
 
846
	// --------------------------------------------------------------------
847
 
848
	/**
849
	 * Image Process Using ImageMagick
850
	 *
851
	 * This function will resize, crop or rotate
852
	 *
853
	 * @param	string
854
	 * @return	bool
855
	 */
856
	public function image_process_imagemagick($action = 'resize')
857
	{
858
		// Do we have a vaild library path?
859
		if ($this->library_path === '')
860
		{
861
			$this->set_error('imglib_libpath_invalid');
862
			return FALSE;
863
		}
864
 
865
		if ( ! preg_match('/convert$/i', $this->library_path))
866
		{
867
			$this->library_path = rtrim($this->library_path, '/').'/convert';
868
		}
869
 
870
		// Execute the command
871
		$cmd = $this->library_path.' -quality '.$this->quality;
872
 
873
		if ($action === 'crop')
874
		{
875
			$cmd .= ' -crop '.$this->width.'x'.$this->height.'+'.$this->x_axis.'+'.$this->y_axis;
876
		}
877
		elseif ($action === 'rotate')
878
		{
879
			$cmd .= ($this->rotation_angle === 'hor' OR $this->rotation_angle === 'vrt')
880
					? ' -flop'
881
					: ' -rotate '.$this->rotation_angle;
882
		}
883
		else // Resize
884
		{
885
			if($this->maintain_ratio === TRUE)
886
			{
887
				$cmd .= ' -resize '.$this->width.'x'.$this->height;
888
			}
889
			else
890
			{
891
				$cmd .= ' -resize '.$this->width.'x'.$this->height.'\!';
892
			}
893
		}
894
 
2049 lars 895
		$cmd .= ' '.escapeshellarg($this->full_src_path).' '.escapeshellarg($this->full_dst_path).' 2>&1';
68 lars 896
 
897
		$retval = 1;
898
		// exec() might be disabled
899
		if (function_usable('exec'))
900
		{
901
			@exec($cmd, $output, $retval);
902
		}
903
 
904
		// Did it work?
905
		if ($retval > 0)
906
		{
907
			$this->set_error('imglib_image_process_failed');
908
			return FALSE;
909
		}
910
 
911
		chmod($this->full_dst_path, $this->file_permissions);
912
 
913
		return TRUE;
914
	}
915
 
916
	// --------------------------------------------------------------------
917
 
918
	/**
919
	 * Image Process Using NetPBM
920
	 *
921
	 * This function will resize, crop or rotate
922
	 *
923
	 * @param	string
924
	 * @return	bool
925
	 */
926
	public function image_process_netpbm($action = 'resize')
927
	{
928
		if ($this->library_path === '')
929
		{
930
			$this->set_error('imglib_libpath_invalid');
931
			return FALSE;
932
		}
933
 
934
		// Build the resizing command
935
		switch ($this->image_type)
936
		{
937
			case 1 :
938
				$cmd_in		= 'giftopnm';
939
				$cmd_out	= 'ppmtogif';
940
				break;
941
			case 2 :
942
				$cmd_in		= 'jpegtopnm';
943
				$cmd_out	= 'ppmtojpeg';
944
				break;
945
			case 3 :
946
				$cmd_in		= 'pngtopnm';
947
				$cmd_out	= 'ppmtopng';
948
				break;
949
		}
950
 
951
		if ($action === 'crop')
952
		{
953
			$cmd_inner = 'pnmcut -left '.$this->x_axis.' -top '.$this->y_axis.' -width '.$this->width.' -height '.$this->height;
954
		}
955
		elseif ($action === 'rotate')
956
		{
957
			switch ($this->rotation_angle)
958
			{
959
				case 90:	$angle = 'r270';
960
					break;
961
				case 180:	$angle = 'r180';
962
					break;
963
				case 270:	$angle = 'r90';
964
					break;
965
				case 'vrt':	$angle = 'tb';
966
					break;
967
				case 'hor':	$angle = 'lr';
968
					break;
969
			}
970
 
971
			$cmd_inner = 'pnmflip -'.$angle.' ';
972
		}
973
		else // Resize
974
		{
975
			$cmd_inner = 'pnmscale -xysize '.$this->width.' '.$this->height;
976
		}
977
 
2107 lars 978
		$cmd = $this->library_path.$cmd_in.' '.escapeshellarg($this->full_src_path).' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp';
68 lars 979
 
980
		$retval = 1;
981
		// exec() might be disabled
982
		if (function_usable('exec'))
983
		{
984
			@exec($cmd, $output, $retval);
985
		}
986
 
987
		// Did it work?
988
		if ($retval > 0)
989
		{
990
			$this->set_error('imglib_image_process_failed');
991
			return FALSE;
992
		}
993
 
994
		// With NetPBM we have to create a temporary image.
995
		// If you try manipulating the original it fails so
996
		// we have to rename the temp file.
997
		copy($this->dest_folder.'netpbm.tmp', $this->full_dst_path);
998
		unlink($this->dest_folder.'netpbm.tmp');
999
		chmod($this->full_dst_path, $this->file_permissions);
1000
 
1001
		return TRUE;
1002
	}
1003
 
1004
	// --------------------------------------------------------------------
1005
 
1006
	/**
1007
	 * Image Rotate Using GD
1008
	 *
1009
	 * @return	bool
1010
	 */
1011
	public function image_rotate_gd()
1012
	{
1013
		// Create the image handle
1014
		if ( ! ($src_img = $this->image_create_gd()))
1015
		{
1016
			return FALSE;
1017
		}
1018
 
1019
		// Set the background color
1020
		// This won't work with transparent PNG files so we are
1021
		// going to have to figure out how to determine the color
1022
		// of the alpha channel in a future release.
1023
 
1024
		$white = imagecolorallocate($src_img, 255, 255, 255);
1025
 
1026
		// Rotate it!
1027
		$dst_img = imagerotate($src_img, $this->rotation_angle, $white);
1028
 
1029
		// Show the image
1030
		if ($this->dynamic_output === TRUE)
1031
		{
1032
			$this->image_display_gd($dst_img);
1033
		}
1034
		elseif ( ! $this->image_save_gd($dst_img)) // ... or save it
1035
		{
1036
			return FALSE;
1037
		}
1038
 
1039
		// Kill the file handles
1040
		imagedestroy($dst_img);
1041
		imagedestroy($src_img);
1042
 
1043
		chmod($this->full_dst_path, $this->file_permissions);
1044
 
1045
		return TRUE;
1046
	}
1047
 
1048
	// --------------------------------------------------------------------
1049
 
1050
	/**
1051
	 * Create Mirror Image using GD
1052
	 *
1053
	 * This function will flip horizontal or vertical
1054
	 *
1055
	 * @return	bool
1056
	 */
1057
	public function image_mirror_gd()
1058
	{
1059
		if ( ! $src_img = $this->image_create_gd())
1060
		{
1061
			return FALSE;
1062
		}
1063
 
1064
		$width  = $this->orig_width;
1065
		$height = $this->orig_height;
1066
 
1067
		if ($this->rotation_angle === 'hor')
1068
		{
1069
			for ($i = 0; $i < $height; $i++)
1070
			{
1071
				$left = 0;
1072
				$right = $width - 1;
1073
 
1074
				while ($left < $right)
1075
				{
1076
					$cl = imagecolorat($src_img, $left, $i);
1077
					$cr = imagecolorat($src_img, $right, $i);
1078
 
1079
					imagesetpixel($src_img, $left, $i, $cr);
1080
					imagesetpixel($src_img, $right, $i, $cl);
1081
 
1082
					$left++;
1083
					$right--;
1084
				}
1085
			}
1086
		}
1087
		else
1088
		{
1089
			for ($i = 0; $i < $width; $i++)
1090
			{
1091
				$top = 0;
1092
				$bottom = $height - 1;
1093
 
1094
				while ($top < $bottom)
1095
				{
1096
					$ct = imagecolorat($src_img, $i, $top);
1097
					$cb = imagecolorat($src_img, $i, $bottom);
1098
 
1099
					imagesetpixel($src_img, $i, $top, $cb);
1100
					imagesetpixel($src_img, $i, $bottom, $ct);
1101
 
1102
					$top++;
1103
					$bottom--;
1104
				}
1105
			}
1106
		}
1107
 
1108
		// Show the image
1109
		if ($this->dynamic_output === TRUE)
1110
		{
1111
			$this->image_display_gd($src_img);
1112
		}
1113
		elseif ( ! $this->image_save_gd($src_img)) // ... or save it
1114
		{
1115
			return FALSE;
1116
		}
1117
 
1118
		// Kill the file handles
1119
		imagedestroy($src_img);
1120
 
1121
		chmod($this->full_dst_path, $this->file_permissions);
1122
 
1123
		return TRUE;
1124
	}
1125
 
1126
	// --------------------------------------------------------------------
1127
 
1128
	/**
1129
	 * Image Watermark
1130
	 *
1131
	 * This is a wrapper function that chooses the type
1132
	 * of watermarking based on the specified preference.
1133
	 *
1134
	 * @return	bool
1135
	 */
1136
	public function watermark()
1137
	{
1138
		return ($this->wm_type === 'overlay') ? $this->overlay_watermark() : $this->text_watermark();
1139
	}
1140
 
1141
	// --------------------------------------------------------------------
1142
 
1143
	/**
1144
	 * Watermark - Graphic Version
1145
	 *
1146
	 * @return	bool
1147
	 */
1148
	public function overlay_watermark()
1149
	{
1150
		if ( ! function_exists('imagecolortransparent'))
1151
		{
1152
			$this->set_error('imglib_gd_required');
1153
			return FALSE;
1154
		}
1155
 
1156
		// Fetch source image properties
1157
		$this->get_image_properties();
1158
 
1159
		// Fetch watermark image properties
1160
		$props		= $this->get_image_properties($this->wm_overlay_path, TRUE);
1161
		$wm_img_type	= $props['image_type'];
1162
		$wm_width	= $props['width'];
1163
		$wm_height	= $props['height'];
1164
 
1165
		// Create two image resources
1166
		$wm_img  = $this->image_create_gd($this->wm_overlay_path, $wm_img_type);
1167
		$src_img = $this->image_create_gd($this->full_src_path);
1168
 
1169
		// Reverse the offset if necessary
1170
		// When the image is positioned at the bottom
1171
		// we don't want the vertical offset to push it
1172
		// further down. We want the reverse, so we'll
1173
		// invert the offset. Same with the horizontal
1174
		// offset when the image is at the right
1175
 
1176
		$this->wm_vrt_alignment = strtoupper($this->wm_vrt_alignment[0]);
1177
		$this->wm_hor_alignment = strtoupper($this->wm_hor_alignment[0]);
1178
 
1179
		if ($this->wm_vrt_alignment === 'B')
1180
			$this->wm_vrt_offset = $this->wm_vrt_offset * -1;
1181
 
1182
		if ($this->wm_hor_alignment === 'R')
1183
			$this->wm_hor_offset = $this->wm_hor_offset * -1;
1184
 
1185
		// Set the base x and y axis values
1186
		$x_axis = $this->wm_hor_offset + $this->wm_padding;
1187
		$y_axis = $this->wm_vrt_offset + $this->wm_padding;
1188
 
1189
		// Set the vertical position
1190
		if ($this->wm_vrt_alignment === 'M')
1191
		{
1192
			$y_axis += ($this->orig_height / 2) - ($wm_height / 2);
1193
		}
1194
		elseif ($this->wm_vrt_alignment === 'B')
1195
		{
1196
			$y_axis += $this->orig_height - $wm_height;
1197
		}
1198
 
1199
		// Set the horizontal position
1200
		if ($this->wm_hor_alignment === 'C')
1201
		{
1202
			$x_axis += ($this->orig_width / 2) - ($wm_width / 2);
1203
		}
1204
		elseif ($this->wm_hor_alignment === 'R')
1205
		{
1206
			$x_axis += $this->orig_width - $wm_width;
1207
		}
1208
 
1209
		// Build the finalized image
1210
		if ($wm_img_type === 3 && function_exists('imagealphablending'))
1211
		{
1212
			@imagealphablending($src_img, TRUE);
1213
		}
1214
 
1215
		// Set RGB values for text and shadow
1216
		$rgba = imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp);
1217
		$alpha = ($rgba & 0x7F000000) >> 24;
1218
 
1219
		// make a best guess as to whether we're dealing with an image with alpha transparency or no/binary transparency
1220
		if ($alpha > 0)
1221
		{
1222
			// copy the image directly, the image's alpha transparency being the sole determinant of blending
1223
			imagecopy($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height);
1224
		}
1225
		else
1226
		{
1227
			// set our RGB value from above to be transparent and merge the images with the specified opacity
1228
			imagecolortransparent($wm_img, imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp));
1229
			imagecopymerge($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height, $this->wm_opacity);
1230
		}
1231
 
1232
		// We can preserve transparency for PNG images
1233
		if ($this->image_type === 3)
1234
		{
1235
			imagealphablending($src_img, FALSE);
1236
			imagesavealpha($src_img, TRUE);
1237
		}
1238
 
1239
		// Output the image
1240
		if ($this->dynamic_output === TRUE)
1241
		{
1242
			$this->image_display_gd($src_img);
1243
		}
1244
		elseif ( ! $this->image_save_gd($src_img)) // ... or save it
1245
		{
1246
			return FALSE;
1247
		}
1248
 
1249
		imagedestroy($src_img);
1250
		imagedestroy($wm_img);
1251
 
1252
		return TRUE;
1253
	}
1254
 
1255
	// --------------------------------------------------------------------
1256
 
1257
	/**
1258
	 * Watermark - Text Version
1259
	 *
1260
	 * @return	bool
1261
	 */
1262
	public function text_watermark()
1263
	{
1264
		if ( ! ($src_img = $this->image_create_gd()))
1265
		{
1266
			return FALSE;
1267
		}
1268
 
1269
		if ($this->wm_use_truetype === TRUE && ! file_exists($this->wm_font_path))
1270
		{
1271
			$this->set_error('imglib_missing_font');
1272
			return FALSE;
1273
		}
1274
 
1275
		// Fetch source image properties
1276
		$this->get_image_properties();
1277
 
1278
		// Reverse the vertical offset
1279
		// When the image is positioned at the bottom
1280
		// we don't want the vertical offset to push it
1281
		// further down. We want the reverse, so we'll
1282
		// invert the offset. Note: The horizontal
1283
		// offset flips itself automatically
1284
 
1285
		if ($this->wm_vrt_alignment === 'B')
1286
		{
1287
			$this->wm_vrt_offset = $this->wm_vrt_offset * -1;
1288
		}
1289
 
1290
		if ($this->wm_hor_alignment === 'R')
1291
		{
1292
			$this->wm_hor_offset = $this->wm_hor_offset * -1;
1293
		}
1294
 
1295
		// Set font width and height
1296
		// These are calculated differently depending on
1297
		// whether we are using the true type font or not
1298
		if ($this->wm_use_truetype === TRUE)
1299
		{
1300
			if (empty($this->wm_font_size))
1301
			{
1302
				$this->wm_font_size = 17;
1303
			}
1304
 
1305
			if (function_exists('imagettfbbox'))
1306
			{
1307
				$temp = imagettfbbox($this->wm_font_size, 0, $this->wm_font_path, $this->wm_text);
1308
				$temp = $temp[2] - $temp[0];
1309
 
1310
				$fontwidth = $temp / strlen($this->wm_text);
1311
			}
1312
			else
1313
			{
1314
				$fontwidth = $this->wm_font_size - ($this->wm_font_size / 4);
1315
			}
1316
 
1317
			$fontheight = $this->wm_font_size;
1318
			$this->wm_vrt_offset += $this->wm_font_size;
1319
		}
1320
		else
1321
		{
1322
			$fontwidth  = imagefontwidth($this->wm_font_size);
1323
			$fontheight = imagefontheight($this->wm_font_size);
1324
		}
1325
 
1326
		// Set base X and Y axis values
1327
		$x_axis = $this->wm_hor_offset + $this->wm_padding;
1328
		$y_axis = $this->wm_vrt_offset + $this->wm_padding;
1329
 
1330
		if ($this->wm_use_drop_shadow === FALSE)
1331
		{
1332
			$this->wm_shadow_distance = 0;
1333
		}
1334
 
1335
		$this->wm_vrt_alignment = strtoupper($this->wm_vrt_alignment[0]);
1336
		$this->wm_hor_alignment = strtoupper($this->wm_hor_alignment[0]);
1337
 
1338
		// Set vertical alignment
1339
		if ($this->wm_vrt_alignment === 'M')
1340
		{
1341
			$y_axis += ($this->orig_height / 2) + ($fontheight / 2);
1342
		}
1343
		elseif ($this->wm_vrt_alignment === 'B')
1344
		{
1345
			$y_axis += $this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight / 2);
1346
		}
1347
 
1348
		// Set horizontal alignment
1349
		if ($this->wm_hor_alignment === 'R')
1350
		{
1351
			$x_axis += $this->orig_width - ($fontwidth * strlen($this->wm_text)) - $this->wm_shadow_distance;
1352
		}
1353
		elseif ($this->wm_hor_alignment === 'C')
1354
		{
1355
			$x_axis += floor(($this->orig_width - ($fontwidth * strlen($this->wm_text))) / 2);
1356
		}
1357
 
1358
		if ($this->wm_use_drop_shadow)
1359
		{
1360
			// Offset from text
1361
			$x_shad = $x_axis + $this->wm_shadow_distance;
1362
			$y_shad = $y_axis + $this->wm_shadow_distance;
1363
 
1364
			/* Set RGB values for shadow
1365
			 *
1366
			 * First character is #, so we don't really need it.
1367
			 * Get the rest of the string and split it into 2-length
1368
			 * hex values:
1369
			 */
1370
			$drp_color = str_split(substr($this->wm_shadow_color, 1, 6), 2);
1371
			$drp_color = imagecolorclosest($src_img, hexdec($drp_color[0]), hexdec($drp_color[1]), hexdec($drp_color[2]));
1372
 
1373
			// Add the shadow to the source image
1374
			if ($this->wm_use_truetype)
1375
			{
1376
				imagettftext($src_img, $this->wm_font_size, 0, $x_shad, $y_shad, $drp_color, $this->wm_font_path, $this->wm_text);
1377
			}
1378
			else
1379
			{
1380
				imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color);
1381
			}
1382
		}
1383
 
1384
		/* Set RGB values for text
1385
		 *
1386
		 * First character is #, so we don't really need it.
1387
		 * Get the rest of the string and split it into 2-length
1388
		 * hex values:
1389
		 */
1390
		$txt_color = str_split(substr($this->wm_font_color, 1, 6), 2);
1391
		$txt_color = imagecolorclosest($src_img, hexdec($txt_color[0]), hexdec($txt_color[1]), hexdec($txt_color[2]));
1392
 
1393
		// Add the text to the source image
1394
		if ($this->wm_use_truetype)
1395
		{
1396
			imagettftext($src_img, $this->wm_font_size, 0, $x_axis, $y_axis, $txt_color, $this->wm_font_path, $this->wm_text);
1397
		}
1398
		else
1399
		{
1400
			imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color);
1401
		}
1402
 
1403
		// We can preserve transparency for PNG images
1404
		if ($this->image_type === 3)
1405
		{
1406
			imagealphablending($src_img, FALSE);
1407
			imagesavealpha($src_img, TRUE);
1408
		}
1409
 
1410
		// Output the final image
1411
		if ($this->dynamic_output === TRUE)
1412
		{
1413
			$this->image_display_gd($src_img);
1414
		}
1415
		else
1416
		{
1417
			$this->image_save_gd($src_img);
1418
		}
1419
 
1420
		imagedestroy($src_img);
1421
 
1422
		return TRUE;
1423
	}
1424
 
1425
	// --------------------------------------------------------------------
1426
 
1427
	/**
1428
	 * Create Image - GD
1429
	 *
1430
	 * This simply creates an image resource handle
1431
	 * based on the type of image being processed
1432
	 *
1433
	 * @param	string
1434
	 * @param	string
1435
	 * @return	resource
1436
	 */
1437
	public function image_create_gd($path = '', $image_type = '')
1438
	{
1439
		if ($path === '')
1440
		{
1441
			$path = $this->full_src_path;
1442
		}
1443
 
1444
		if ($image_type === '')
1445
		{
1446
			$image_type = $this->image_type;
1447
		}
1448
 
1449
		switch ($image_type)
1450
		{
1451
			case 1:
1452
				if ( ! function_exists('imagecreatefromgif'))
1453
				{
1454
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
1455
					return FALSE;
1456
				}
1457
 
1458
				return imagecreatefromgif($path);
1459
			case 2:
1460
				if ( ! function_exists('imagecreatefromjpeg'))
1461
				{
1462
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
1463
					return FALSE;
1464
				}
1465
 
1466
				return imagecreatefromjpeg($path);
1467
			case 3:
1468
				if ( ! function_exists('imagecreatefrompng'))
1469
				{
1470
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
1471
					return FALSE;
1472
				}
1473
 
1474
				return imagecreatefrompng($path);
1475
			default:
1476
				$this->set_error(array('imglib_unsupported_imagecreate'));
1477
				return FALSE;
1478
		}
1479
	}
1480
 
1481
	// --------------------------------------------------------------------
1482
 
1483
	/**
1484
	 * Write image file to disk - GD
1485
	 *
1486
	 * Takes an image resource as input and writes the file
1487
	 * to the specified destination
1488
	 *
1489
	 * @param	resource
1490
	 * @return	bool
1491
	 */
1492
	public function image_save_gd($resource)
1493
	{
1494
		switch ($this->image_type)
1495
		{
1496
			case 1:
1497
				if ( ! function_exists('imagegif'))
1498
				{
1499
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
1500
					return FALSE;
1501
				}
1502
 
1503
				if ( ! @imagegif($resource, $this->full_dst_path))
1504
				{
1505
					$this->set_error('imglib_save_failed');
1506
					return FALSE;
1507
				}
1508
			break;
1509
			case 2:
1510
				if ( ! function_exists('imagejpeg'))
1511
				{
1512
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
1513
					return FALSE;
1514
				}
1515
 
1516
				if ( ! @imagejpeg($resource, $this->full_dst_path, $this->quality))
1517
				{
1518
					$this->set_error('imglib_save_failed');
1519
					return FALSE;
1520
				}
1521
			break;
1522
			case 3:
1523
				if ( ! function_exists('imagepng'))
1524
				{
1525
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
1526
					return FALSE;
1527
				}
1528
 
1529
				if ( ! @imagepng($resource, $this->full_dst_path))
1530
				{
1531
					$this->set_error('imglib_save_failed');
1532
					return FALSE;
1533
				}
1534
			break;
1535
			default:
1536
				$this->set_error(array('imglib_unsupported_imagecreate'));
1537
				return FALSE;
1538
			break;
1539
		}
1540
 
1541
		return TRUE;
1542
	}
1543
 
1544
	// --------------------------------------------------------------------
1545
 
1546
	/**
1547
	 * Dynamically outputs an image
1548
	 *
1549
	 * @param	resource
1550
	 * @return	void
1551
	 */
1552
	public function image_display_gd($resource)
1553
	{
1554
		header('Content-Disposition: filename='.$this->source_image.';');
1555
		header('Content-Type: '.$this->mime_type);
1556
		header('Content-Transfer-Encoding: binary');
1557
		header('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT');
1558
 
1559
		switch ($this->image_type)
1560
		{
1561
			case 1	:	imagegif($resource);
1562
				break;
1563
			case 2	:	imagejpeg($resource, NULL, $this->quality);
1564
				break;
1565
			case 3	:	imagepng($resource);
1566
				break;
1567
			default:	echo 'Unable to display the image';
1568
				break;
1569
		}
1570
	}
1571
 
1572
	// --------------------------------------------------------------------
1573
 
1574
	/**
1575
	 * Re-proportion Image Width/Height
1576
	 *
1577
	 * When creating thumbs, the desired width/height
1578
	 * can end up warping the image due to an incorrect
1579
	 * ratio between the full-sized image and the thumb.
1580
	 *
1581
	 * This function lets us re-proportion the width/height
1582
	 * if users choose to maintain the aspect ratio when resizing.
1583
	 *
1584
	 * @return	void
1585
	 */
1586
	public function image_reproportion()
1587
	{
1588
		if (($this->width === 0 && $this->height === 0) OR $this->orig_width === 0 OR $this->orig_height === 0
1589
			OR ( ! ctype_digit((string) $this->width) && ! ctype_digit((string) $this->height))
1590
			OR ! ctype_digit((string) $this->orig_width) OR ! ctype_digit((string) $this->orig_height))
1591
		{
1592
			return;
1593
		}
1594
 
1595
		// Sanitize
1596
		$this->width = (int) $this->width;
1597
		$this->height = (int) $this->height;
1598
 
1599
		if ($this->master_dim !== 'width' && $this->master_dim !== 'height')
1600
		{
1601
			if ($this->width > 0 && $this->height > 0)
1602
			{
1603
				$this->master_dim = ((($this->orig_height/$this->orig_width) - ($this->height/$this->width)) < 0)
1604
							? 'width' : 'height';
1605
			}
1606
			else
1607
			{
1608
				$this->master_dim = ($this->height === 0) ? 'width' : 'height';
1609
			}
1610
		}
1611
		elseif (($this->master_dim === 'width' && $this->width === 0)
1612
			OR ($this->master_dim === 'height' && $this->height === 0))
1613
		{
1614
			return;
1615
		}
1616
 
1617
		if ($this->master_dim === 'width')
1618
		{
1619
			$this->height = (int) ceil($this->width*$this->orig_height/$this->orig_width);
1620
		}
1621
		else
1622
		{
1623
			$this->width = (int) ceil($this->orig_width*$this->height/$this->orig_height);
1624
		}
1625
	}
1626
 
1627
	// --------------------------------------------------------------------
1628
 
1629
	/**
1630
	 * Get image properties
1631
	 *
1632
	 * A helper function that gets info about the file
1633
	 *
1634
	 * @param	string
1635
	 * @param	bool
1636
	 * @return	mixed
1637
	 */
1638
	public function get_image_properties($path = '', $return = FALSE)
1639
	{
1640
		// For now we require GD but we should
1641
		// find a way to determine this using IM or NetPBM
1642
 
1643
		if ($path === '')
1644
		{
1645
			$path = $this->full_src_path;
1646
		}
1647
 
1648
		if ( ! file_exists($path))
1649
		{
1650
			$this->set_error('imglib_invalid_path');
1651
			return FALSE;
1652
		}
1653
 
1654
		$vals = getimagesize($path);
2049 lars 1655
		if ($vals === FALSE)
1656
		{
1657
			$this->set_error('imglib_invalid_image');
1658
			return FALSE;
1659
		}
1660
 
68 lars 1661
		$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
2049 lars 1662
		$mime = isset($types[$vals[2]]) ? 'image/'.$types[$vals[2]] : 'image/jpg';
68 lars 1663
 
1664
		if ($return === TRUE)
1665
		{
1666
			return array(
2049 lars 1667
				'width'      => $vals[0],
1668
				'height'     => $vals[1],
1669
				'image_type' => $vals[2],
1670
				'size_str'   => $vals[3],
1671
				'mime_type'  => $mime
1672
			);
68 lars 1673
		}
1674
 
2049 lars 1675
		$this->orig_width  = $vals[0];
1676
		$this->orig_height = $vals[1];
1677
		$this->image_type  = $vals[2];
1678
		$this->size_str    = $vals[3];
1679
		$this->mime_type   = $mime;
68 lars 1680
 
1681
		return TRUE;
1682
	}
1683
 
1684
	// --------------------------------------------------------------------
1685
 
1686
	/**
1687
	 * Size calculator
1688
	 *
1689
	 * This function takes a known width x height and
1690
	 * recalculates it to a new size. Only one
1691
	 * new variable needs to be known
1692
	 *
1693
	 *	$props = array(
1694
	 *			'width'		=> $width,
1695
	 *			'height'	=> $height,
1696
	 *			'new_width'	=> 40,
1697
	 *			'new_height'	=> ''
1698
	 *		);
1699
	 *
1700
	 * @param	array
1701
	 * @return	array
1702
	 */
1703
	public function size_calculator($vals)
1704
	{
1705
		if ( ! is_array($vals))
1706
		{
1707
			return;
1708
		}
1709
 
1710
		$allowed = array('new_width', 'new_height', 'width', 'height');
1711
 
1712
		foreach ($allowed as $item)
1713
		{
1714
			if (empty($vals[$item]))
1715
			{
1716
				$vals[$item] = 0;
1717
			}
1718
		}
1719
 
1720
		if ($vals['width'] === 0 OR $vals['height'] === 0)
1721
		{
1722
			return $vals;
1723
		}
1724
 
1725
		if ($vals['new_width'] === 0)
1726
		{
1727
			$vals['new_width'] = ceil($vals['width']*$vals['new_height']/$vals['height']);
1728
		}
1729
		elseif ($vals['new_height'] === 0)
1730
		{
1731
			$vals['new_height'] = ceil($vals['new_width']*$vals['height']/$vals['width']);
1732
		}
1733
 
1734
		return $vals;
1735
	}
1736
 
1737
	// --------------------------------------------------------------------
1738
 
1739
	/**
1740
	 * Explode source_image
1741
	 *
1742
	 * This is a helper function that extracts the extension
1743
	 * from the source_image.  This function lets us deal with
1744
	 * source_images with multiple periods, like: my.cool.jpg
1745
	 * It returns an associative array with two elements:
1746
	 * $array['ext']  = '.jpg';
1747
	 * $array['name'] = 'my.cool';
1748
	 *
1749
	 * @param	array
1750
	 * @return	array
1751
	 */
1752
	public function explode_name($source_image)
1753
	{
1754
		$ext = strrchr($source_image, '.');
1755
		$name = ($ext === FALSE) ? $source_image : substr($source_image, 0, -strlen($ext));
1756
 
1757
		return array('ext' => $ext, 'name' => $name);
1758
	}
1759
 
1760
	// --------------------------------------------------------------------
1761
 
1762
	/**
1763
	 * Is GD Installed?
1764
	 *
1765
	 * @return	bool
1766
	 */
1767
	public function gd_loaded()
1768
	{
1769
		if ( ! extension_loaded('gd'))
1770
		{
1771
			/* As it is stated in the PHP manual, dl() is not always available
1772
			 * and even if so - it could generate an E_WARNING message on failure
1773
			 */
1774
			return (function_exists('dl') && @dl('gd.so'));
1775
		}
1776
 
1777
		return TRUE;
1778
	}
1779
 
1780
	// --------------------------------------------------------------------
1781
 
1782
	/**
1783
	 * Get GD version
1784
	 *
1785
	 * @return	mixed
1786
	 */
1787
	public function gd_version()
1788
	{
1789
		if (function_exists('gd_info'))
1790
		{
1791
			$gd_version = @gd_info();
1792
			return preg_replace('/\D/', '', $gd_version['GD Version']);
1793
		}
1794
 
1795
		return FALSE;
1796
	}
1797
 
1798
	// --------------------------------------------------------------------
1799
 
1800
	/**
1801
	 * Set error message
1802
	 *
1803
	 * @param	string
1804
	 * @return	void
1805
	 */
1806
	public function set_error($msg)
1807
	{
1808
		$CI =& get_instance();
1809
		$CI->lang->load('imglib');
1810
 
1811
		if (is_array($msg))
1812
		{
1813
			foreach ($msg as $val)
1814
			{
1815
				$msg = ($CI->lang->line($val) === FALSE) ? $val : $CI->lang->line($val);
1816
				$this->error_msg[] = $msg;
1817
				log_message('error', $msg);
1818
			}
1819
		}
1820
		else
1821
		{
1822
			$msg = ($CI->lang->line($msg) === FALSE) ? $msg : $CI->lang->line($msg);
1823
			$this->error_msg[] = $msg;
1824
			log_message('error', $msg);
1825
		}
1826
	}
1827
 
1828
	// --------------------------------------------------------------------
1829
 
1830
	/**
1831
	 * Show error messages
1832
	 *
1833
	 * @param	string
1834
	 * @param	string
1835
	 * @return	string
1836
	 */
1837
	public function display_errors($open = '<p>', $close = '</p>')
1838
	{
1839
		return (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : '';
1840
	}
1841
 
1842
}