Subversion-Projekte lars-tiefland.ci

Revision

Revision 68 | Revision 2049 | 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
 *
9
 * Copyright (c) 2014 - 2016, British Columbia Institute of Technology
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/)
32
 * @copyright	Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/)
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
 
395
		log_message('info', 'Image Lib Class Initialized');
396
	}
397
 
398
	// --------------------------------------------------------------------
399
 
400
	/**
401
	 * Initialize image properties
402
	 *
403
	 * Resets values in case this class is used in a loop
404
	 *
405
	 * @return	void
406
	 */
407
	public function clear()
408
	{
409
		$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');
410
 
411
		foreach ($props as $val)
412
		{
413
			$this->$val = '';
414
		}
415
 
416
		$this->image_library 		= 'gd2';
417
		$this->dynamic_output 		= FALSE;
418
		$this->quality 				= 90;
419
		$this->create_thumb 		= FALSE;
420
		$this->thumb_marker 		= '_thumb';
421
		$this->maintain_ratio 		= TRUE;
422
		$this->master_dim 			= 'auto';
423
		$this->wm_type 				= 'text';
424
		$this->wm_x_transp 			= 4;
425
		$this->wm_y_transp 			= 4;
426
		$this->wm_font_size 		= 17;
427
		$this->wm_vrt_alignment 	= 'B';
428
		$this->wm_hor_alignment 	= 'C';
429
		$this->wm_padding 			= 0;
430
		$this->wm_hor_offset 		= 0;
431
		$this->wm_vrt_offset 		= 0;
432
		$this->wm_font_color		= '#ffffff';
433
		$this->wm_shadow_distance 	= 2;
434
		$this->wm_opacity 			= 50;
435
		$this->create_fnc 			= 'imagecreatetruecolor';
436
		$this->copy_fnc 			= 'imagecopyresampled';
437
		$this->error_msg 			= array();
438
		$this->wm_use_drop_shadow 	= FALSE;
439
		$this->wm_use_truetype 		= FALSE;
440
	}
441
 
442
	// --------------------------------------------------------------------
443
 
444
	/**
445
	 * initialize image preferences
446
	 *
447
	 * @param	array
448
	 * @return	bool
449
	 */
450
	public function initialize($props = array())
451
	{
452
		// Convert array elements into class variables
453
		if (count($props) > 0)
454
		{
455
			foreach ($props as $key => $val)
456
			{
457
				if (property_exists($this, $key))
458
				{
459
					if (in_array($key, array('wm_font_color', 'wm_shadow_color'), TRUE))
460
					{
461
						if (preg_match('/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i', $val, $matches))
462
						{
463
							/* $matches[1] contains our hex color value, but it might be
464
							 * both in the full 6-length format or the shortened 3-length
465
							 * value.
466
							 * We'll later need the full version, so we keep it if it's
467
							 * already there and if not - we'll convert to it. We can
468
							 * access string characters by their index as in an array,
469
							 * so we'll do that and use concatenation to form the final
470
							 * value:
471
							 */
472
							$val = (strlen($matches[1]) === 6)
473
								? '#'.$matches[1]
474
								: '#'.$matches[1][0].$matches[1][0].$matches[1][1].$matches[1][1].$matches[1][2].$matches[1][2];
475
						}
476
						else
477
						{
478
							continue;
479
						}
480
					}
481
					elseif (in_array($key, array('width', 'height'), TRUE) && ! ctype_digit((string) $val))
482
					{
483
						continue;
484
					}
485
 
486
					$this->$key = $val;
487
				}
488
			}
489
		}
490
 
491
		// Is there a source image? If not, there's no reason to continue
492
		if ($this->source_image === '')
493
		{
494
			$this->set_error('imglib_source_image_required');
495
			return FALSE;
496
		}
497
 
498
		/* Is getimagesize() available?
499
		 *
500
		 * We use it to determine the image properties (width/height).
501
		 * Note: We need to figure out how to determine image
502
		 * properties using ImageMagick and NetPBM
503
		 */
504
		if ( ! function_exists('getimagesize'))
505
		{
506
			$this->set_error('imglib_gd_required_for_props');
507
			return FALSE;
508
		}
509
 
510
		$this->image_library = strtolower($this->image_library);
511
 
512
		/* Set the full server path
513
		 *
514
		 * The source image may or may not contain a path.
515
		 * Either way, we'll try use realpath to generate the
516
		 * full server path in order to more reliably read it.
517
		 */
518
		if (($full_source_path = realpath($this->source_image)) !== FALSE)
519
		{
520
			$full_source_path = str_replace('\\', '/', $full_source_path);
521
		}
522
		else
523
		{
524
			$full_source_path = $this->source_image;
525
		}
526
 
527
		$x = explode('/', $full_source_path);
528
		$this->source_image = end($x);
529
		$this->source_folder = str_replace($this->source_image, '', $full_source_path);
530
 
531
		// Set the Image Properties
532
		if ( ! $this->get_image_properties($this->source_folder.$this->source_image))
533
		{
534
			return FALSE;
535
		}
536
 
537
		/*
538
		 * Assign the "new" image name/path
539
		 *
540
		 * If the user has set a "new_image" name it means
541
		 * we are making a copy of the source image. If not
542
		 * it means we are altering the original. We'll
543
		 * set the destination filename and path accordingly.
544
		 */
545
		if ($this->new_image === '')
546
		{
547
			$this->dest_image = $this->source_image;
548
			$this->dest_folder = $this->source_folder;
549
		}
550
		elseif (strpos($this->new_image, '/') === FALSE)
551
		{
552
			$this->dest_folder = $this->source_folder;
553
			$this->dest_image = $this->new_image;
554
		}
555
		else
556
		{
557
			if (strpos($this->new_image, '/') === FALSE && strpos($this->new_image, '\\') === FALSE)
558
			{
559
				$full_dest_path = str_replace('\\', '/', realpath($this->new_image));
560
			}
561
			else
562
			{
563
				$full_dest_path = $this->new_image;
564
			}
565
 
566
			// Is there a file name?
567
			if ( ! preg_match('#\.(jpg|jpeg|gif|png)$#i', $full_dest_path))
568
			{
569
				$this->dest_folder = $full_dest_path.'/';
570
				$this->dest_image = $this->source_image;
571
			}
572
			else
573
			{
574
				$x = explode('/', $full_dest_path);
575
				$this->dest_image = end($x);
576
				$this->dest_folder = str_replace($this->dest_image, '', $full_dest_path);
577
			}
578
		}
579
 
580
		/* Compile the finalized filenames/paths
581
		 *
582
		 * We'll create two master strings containing the
583
		 * full server path to the source image and the
584
		 * full server path to the destination image.
585
		 * We'll also split the destination image name
586
		 * so we can insert the thumbnail marker if needed.
587
		 */
588
		if ($this->create_thumb === FALSE OR $this->thumb_marker === '')
589
		{
590
			$this->thumb_marker = '';
591
		}
592
 
593
		$xp = $this->explode_name($this->dest_image);
594
 
595
		$filename = $xp['name'];
596
		$file_ext = $xp['ext'];
597
 
598
		$this->full_src_path = $this->source_folder.$this->source_image;
599
		$this->full_dst_path = $this->dest_folder.$filename.$this->thumb_marker.$file_ext;
600
 
601
		/* Should we maintain image proportions?
602
		 *
603
		 * When creating thumbs or copies, the target width/height
604
		 * might not be in correct proportion with the source
605
		 * image's width/height. We'll recalculate it here.
606
		 */
607
		if ($this->maintain_ratio === TRUE && ($this->width !== 0 OR $this->height !== 0))
608
		{
609
			$this->image_reproportion();
610
		}
611
 
612
		/* Was a width and height specified?
613
		 *
614
		 * If the destination width/height was not submitted we
615
		 * will use the values from the actual file
616
		 */
617
		if ($this->width === '')
618
		{
619
			$this->width = $this->orig_width;
620
		}
621
 
622
		if ($this->height === '')
623
		{
624
			$this->height = $this->orig_height;
625
		}
626
 
627
		// Set the quality
628
		$this->quality = trim(str_replace('%', '', $this->quality));
629
 
630
		if ($this->quality === '' OR $this->quality === 0 OR ! ctype_digit($this->quality))
631
		{
632
			$this->quality = 90;
633
		}
634
 
635
		// Set the x/y coordinates
636
		is_numeric($this->x_axis) OR $this->x_axis = 0;
637
		is_numeric($this->y_axis) OR $this->y_axis = 0;
638
 
639
		// Watermark-related Stuff...
640
		if ($this->wm_overlay_path !== '')
641
		{
642
			$this->wm_overlay_path = str_replace('\\', '/', realpath($this->wm_overlay_path));
643
		}
644
 
645
		if ($this->wm_shadow_color !== '')
646
		{
647
			$this->wm_use_drop_shadow = TRUE;
648
		}
649
		elseif ($this->wm_use_drop_shadow === TRUE && $this->wm_shadow_color === '')
650
		{
651
			$this->wm_use_drop_shadow = FALSE;
652
		}
653
 
654
		if ($this->wm_font_path !== '')
655
		{
656
			$this->wm_use_truetype = TRUE;
657
		}
658
 
659
		return TRUE;
660
	}
661
 
662
	// --------------------------------------------------------------------
663
 
664
	/**
665
	 * Image Resize
666
	 *
667
	 * This is a wrapper function that chooses the proper
668
	 * resize function based on the protocol specified
669
	 *
670
	 * @return	bool
671
	 */
672
	public function resize()
673
	{
674
		$protocol = ($this->image_library === 'gd2') ? 'image_process_gd' : 'image_process_'.$this->image_library;
675
		return $this->$protocol('resize');
676
	}
677
 
678
	// --------------------------------------------------------------------
679
 
680
	/**
681
	 * Image Crop
682
	 *
683
	 * This is a wrapper function that chooses the proper
684
	 * cropping function based on the protocol specified
685
	 *
686
	 * @return	bool
687
	 */
688
	public function crop()
689
	{
690
		$protocol = ($this->image_library === 'gd2') ? 'image_process_gd' : 'image_process_'.$this->image_library;
691
		return $this->$protocol('crop');
692
	}
693
 
694
	// --------------------------------------------------------------------
695
 
696
	/**
697
	 * Image Rotate
698
	 *
699
	 * This is a wrapper function that chooses the proper
700
	 * rotation function based on the protocol specified
701
	 *
702
	 * @return	bool
703
	 */
704
	public function rotate()
705
	{
706
		// Allowed rotation values
707
		$degs = array(90, 180, 270, 'vrt', 'hor');
708
 
709
		if ($this->rotation_angle === '' OR ! in_array($this->rotation_angle, $degs))
710
		{
711
			$this->set_error('imglib_rotation_angle_required');
712
			return FALSE;
713
		}
714
 
715
		// Reassign the width and height
716
		if ($this->rotation_angle === 90 OR $this->rotation_angle === 270)
717
		{
718
			$this->width	= $this->orig_height;
719
			$this->height	= $this->orig_width;
720
		}
721
		else
722
		{
723
			$this->width	= $this->orig_width;
724
			$this->height	= $this->orig_height;
725
		}
726
 
727
		// Choose resizing function
728
		if ($this->image_library === 'imagemagick' OR $this->image_library === 'netpbm')
729
		{
730
			$protocol = 'image_process_'.$this->image_library;
731
			return $this->$protocol('rotate');
732
		}
733
 
734
		return ($this->rotation_angle === 'hor' OR $this->rotation_angle === 'vrt')
735
			? $this->image_mirror_gd()
736
			: $this->image_rotate_gd();
737
	}
738
 
739
	// --------------------------------------------------------------------
740
 
741
	/**
742
	 * Image Process Using GD/GD2
743
	 *
744
	 * This function will resize or crop
745
	 *
746
	 * @param	string
747
	 * @return	bool
748
	 */
749
	public function image_process_gd($action = 'resize')
750
	{
751
		$v2_override = FALSE;
752
 
753
		// If the target width/height match the source, AND if the new file name is not equal to the old file name
754
		// we'll simply make a copy of the original with the new name... assuming dynamic rendering is off.
755
		if ($this->dynamic_output === FALSE && $this->orig_width === $this->width && $this->orig_height === $this->height)
756
		{
757
			if ($this->source_image !== $this->new_image && @copy($this->full_src_path, $this->full_dst_path))
758
			{
759
				chmod($this->full_dst_path, $this->file_permissions);
760
			}
761
 
762
			return TRUE;
763
		}
764
 
765
		// Let's set up our values based on the action
766
		if ($action === 'crop')
767
		{
768
			// Reassign the source width/height if cropping
769
			$this->orig_width  = $this->width;
770
			$this->orig_height = $this->height;
771
 
772
			// GD 2.0 has a cropping bug so we'll test for it
773
			if ($this->gd_version() !== FALSE)
774
			{
775
				$gd_version = str_replace('0', '', $this->gd_version());
776
				$v2_override = ($gd_version == 2);
777
			}
778
		}
779
		else
780
		{
781
			// If resizing the x/y axis must be zero
782
			$this->x_axis = 0;
783
			$this->y_axis = 0;
784
		}
785
 
786
		// Create the image handle
787
		if ( ! ($src_img = $this->image_create_gd()))
788
		{
789
			return FALSE;
790
		}
791
 
792
		/* Create the image
793
		 *
794
		 * Old conditional which users report cause problems with shared GD libs who report themselves as "2.0 or greater"
795
		 * it appears that this is no longer the issue that it was in 2004, so we've removed it, retaining it in the comment
796
		 * below should that ever prove inaccurate.
797
		 *
798
		 * if ($this->image_library === 'gd2' && function_exists('imagecreatetruecolor') && $v2_override === FALSE)
799
		 */
800
		if ($this->image_library === 'gd2' && function_exists('imagecreatetruecolor'))
801
		{
802
			$create	= 'imagecreatetruecolor';
803
			$copy	= 'imagecopyresampled';
804
		}
805
		else
806
		{
807
			$create	= 'imagecreate';
808
			$copy	= 'imagecopyresized';
809
		}
810
 
811
		$dst_img = $create($this->width, $this->height);
812
 
813
		if ($this->image_type === 3) // png we can actually preserve transparency
814
		{
815
			imagealphablending($dst_img, FALSE);
816
			imagesavealpha($dst_img, TRUE);
817
		}
818
 
819
		$copy($dst_img, $src_img, 0, 0, $this->x_axis, $this->y_axis, $this->width, $this->height, $this->orig_width, $this->orig_height);
820
 
821
		// Show the image
822
		if ($this->dynamic_output === TRUE)
823
		{
824
			$this->image_display_gd($dst_img);
825
		}
826
		elseif ( ! $this->image_save_gd($dst_img)) // Or save it
827
		{
828
			return FALSE;
829
		}
830
 
831
		// Kill the file handles
832
		imagedestroy($dst_img);
833
		imagedestroy($src_img);
834
 
835
		chmod($this->full_dst_path, $this->file_permissions);
836
 
837
		return TRUE;
838
	}
839
 
840
	// --------------------------------------------------------------------
841
 
842
	/**
843
	 * Image Process Using ImageMagick
844
	 *
845
	 * This function will resize, crop or rotate
846
	 *
847
	 * @param	string
848
	 * @return	bool
849
	 */
850
	public function image_process_imagemagick($action = 'resize')
851
	{
852
		// Do we have a vaild library path?
853
		if ($this->library_path === '')
854
		{
855
			$this->set_error('imglib_libpath_invalid');
856
			return FALSE;
857
		}
858
 
859
		if ( ! preg_match('/convert$/i', $this->library_path))
860
		{
861
			$this->library_path = rtrim($this->library_path, '/').'/convert';
862
		}
863
 
864
		// Execute the command
865
		$cmd = $this->library_path.' -quality '.$this->quality;
866
 
867
		if ($action === 'crop')
868
		{
869
			$cmd .= ' -crop '.$this->width.'x'.$this->height.'+'.$this->x_axis.'+'.$this->y_axis;
870
		}
871
		elseif ($action === 'rotate')
872
		{
873
			$cmd .= ($this->rotation_angle === 'hor' OR $this->rotation_angle === 'vrt')
874
					? ' -flop'
875
					: ' -rotate '.$this->rotation_angle;
876
		}
877
		else // Resize
878
		{
879
			if($this->maintain_ratio === TRUE)
880
			{
881
				$cmd .= ' -resize '.$this->width.'x'.$this->height;
882
			}
883
			else
884
			{
885
				$cmd .= ' -resize '.$this->width.'x'.$this->height.'\!';
886
			}
887
		}
888
 
1257 lars 889
		$cmd .= escapeshellarg($this->full_src_path).' '.escapeshellarg($this->full_dst_path).' 2>&1';
68 lars 890
 
891
		$retval = 1;
892
		// exec() might be disabled
893
		if (function_usable('exec'))
894
		{
895
			@exec($cmd, $output, $retval);
896
		}
897
 
898
		// Did it work?
899
		if ($retval > 0)
900
		{
901
			$this->set_error('imglib_image_process_failed');
902
			return FALSE;
903
		}
904
 
905
		chmod($this->full_dst_path, $this->file_permissions);
906
 
907
		return TRUE;
908
	}
909
 
910
	// --------------------------------------------------------------------
911
 
912
	/**
913
	 * Image Process Using NetPBM
914
	 *
915
	 * This function will resize, crop or rotate
916
	 *
917
	 * @param	string
918
	 * @return	bool
919
	 */
920
	public function image_process_netpbm($action = 'resize')
921
	{
922
		if ($this->library_path === '')
923
		{
924
			$this->set_error('imglib_libpath_invalid');
925
			return FALSE;
926
		}
927
 
928
		// Build the resizing command
929
		switch ($this->image_type)
930
		{
931
			case 1 :
932
				$cmd_in		= 'giftopnm';
933
				$cmd_out	= 'ppmtogif';
934
				break;
935
			case 2 :
936
				$cmd_in		= 'jpegtopnm';
937
				$cmd_out	= 'ppmtojpeg';
938
				break;
939
			case 3 :
940
				$cmd_in		= 'pngtopnm';
941
				$cmd_out	= 'ppmtopng';
942
				break;
943
		}
944
 
945
		if ($action === 'crop')
946
		{
947
			$cmd_inner = 'pnmcut -left '.$this->x_axis.' -top '.$this->y_axis.' -width '.$this->width.' -height '.$this->height;
948
		}
949
		elseif ($action === 'rotate')
950
		{
951
			switch ($this->rotation_angle)
952
			{
953
				case 90:	$angle = 'r270';
954
					break;
955
				case 180:	$angle = 'r180';
956
					break;
957
				case 270:	$angle = 'r90';
958
					break;
959
				case 'vrt':	$angle = 'tb';
960
					break;
961
				case 'hor':	$angle = 'lr';
962
					break;
963
			}
964
 
965
			$cmd_inner = 'pnmflip -'.$angle.' ';
966
		}
967
		else // Resize
968
		{
969
			$cmd_inner = 'pnmscale -xysize '.$this->width.' '.$this->height;
970
		}
971
 
972
		$cmd = $this->library_path.$cmd_in.' '.$this->full_src_path.' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp';
973
 
974
		$retval = 1;
975
		// exec() might be disabled
976
		if (function_usable('exec'))
977
		{
978
			@exec($cmd, $output, $retval);
979
		}
980
 
981
		// Did it work?
982
		if ($retval > 0)
983
		{
984
			$this->set_error('imglib_image_process_failed');
985
			return FALSE;
986
		}
987
 
988
		// With NetPBM we have to create a temporary image.
989
		// If you try manipulating the original it fails so
990
		// we have to rename the temp file.
991
		copy($this->dest_folder.'netpbm.tmp', $this->full_dst_path);
992
		unlink($this->dest_folder.'netpbm.tmp');
993
		chmod($this->full_dst_path, $this->file_permissions);
994
 
995
		return TRUE;
996
	}
997
 
998
	// --------------------------------------------------------------------
999
 
1000
	/**
1001
	 * Image Rotate Using GD
1002
	 *
1003
	 * @return	bool
1004
	 */
1005
	public function image_rotate_gd()
1006
	{
1007
		// Create the image handle
1008
		if ( ! ($src_img = $this->image_create_gd()))
1009
		{
1010
			return FALSE;
1011
		}
1012
 
1013
		// Set the background color
1014
		// This won't work with transparent PNG files so we are
1015
		// going to have to figure out how to determine the color
1016
		// of the alpha channel in a future release.
1017
 
1018
		$white = imagecolorallocate($src_img, 255, 255, 255);
1019
 
1020
		// Rotate it!
1021
		$dst_img = imagerotate($src_img, $this->rotation_angle, $white);
1022
 
1023
		// Show the image
1024
		if ($this->dynamic_output === TRUE)
1025
		{
1026
			$this->image_display_gd($dst_img);
1027
		}
1028
		elseif ( ! $this->image_save_gd($dst_img)) // ... or save it
1029
		{
1030
			return FALSE;
1031
		}
1032
 
1033
		// Kill the file handles
1034
		imagedestroy($dst_img);
1035
		imagedestroy($src_img);
1036
 
1037
		chmod($this->full_dst_path, $this->file_permissions);
1038
 
1039
		return TRUE;
1040
	}
1041
 
1042
	// --------------------------------------------------------------------
1043
 
1044
	/**
1045
	 * Create Mirror Image using GD
1046
	 *
1047
	 * This function will flip horizontal or vertical
1048
	 *
1049
	 * @return	bool
1050
	 */
1051
	public function image_mirror_gd()
1052
	{
1053
		if ( ! $src_img = $this->image_create_gd())
1054
		{
1055
			return FALSE;
1056
		}
1057
 
1058
		$width  = $this->orig_width;
1059
		$height = $this->orig_height;
1060
 
1061
		if ($this->rotation_angle === 'hor')
1062
		{
1063
			for ($i = 0; $i < $height; $i++)
1064
			{
1065
				$left = 0;
1066
				$right = $width - 1;
1067
 
1068
				while ($left < $right)
1069
				{
1070
					$cl = imagecolorat($src_img, $left, $i);
1071
					$cr = imagecolorat($src_img, $right, $i);
1072
 
1073
					imagesetpixel($src_img, $left, $i, $cr);
1074
					imagesetpixel($src_img, $right, $i, $cl);
1075
 
1076
					$left++;
1077
					$right--;
1078
				}
1079
			}
1080
		}
1081
		else
1082
		{
1083
			for ($i = 0; $i < $width; $i++)
1084
			{
1085
				$top = 0;
1086
				$bottom = $height - 1;
1087
 
1088
				while ($top < $bottom)
1089
				{
1090
					$ct = imagecolorat($src_img, $i, $top);
1091
					$cb = imagecolorat($src_img, $i, $bottom);
1092
 
1093
					imagesetpixel($src_img, $i, $top, $cb);
1094
					imagesetpixel($src_img, $i, $bottom, $ct);
1095
 
1096
					$top++;
1097
					$bottom--;
1098
				}
1099
			}
1100
		}
1101
 
1102
		// Show the image
1103
		if ($this->dynamic_output === TRUE)
1104
		{
1105
			$this->image_display_gd($src_img);
1106
		}
1107
		elseif ( ! $this->image_save_gd($src_img)) // ... or save it
1108
		{
1109
			return FALSE;
1110
		}
1111
 
1112
		// Kill the file handles
1113
		imagedestroy($src_img);
1114
 
1115
		chmod($this->full_dst_path, $this->file_permissions);
1116
 
1117
		return TRUE;
1118
	}
1119
 
1120
	// --------------------------------------------------------------------
1121
 
1122
	/**
1123
	 * Image Watermark
1124
	 *
1125
	 * This is a wrapper function that chooses the type
1126
	 * of watermarking based on the specified preference.
1127
	 *
1128
	 * @return	bool
1129
	 */
1130
	public function watermark()
1131
	{
1132
		return ($this->wm_type === 'overlay') ? $this->overlay_watermark() : $this->text_watermark();
1133
	}
1134
 
1135
	// --------------------------------------------------------------------
1136
 
1137
	/**
1138
	 * Watermark - Graphic Version
1139
	 *
1140
	 * @return	bool
1141
	 */
1142
	public function overlay_watermark()
1143
	{
1144
		if ( ! function_exists('imagecolortransparent'))
1145
		{
1146
			$this->set_error('imglib_gd_required');
1147
			return FALSE;
1148
		}
1149
 
1150
		// Fetch source image properties
1151
		$this->get_image_properties();
1152
 
1153
		// Fetch watermark image properties
1154
		$props		= $this->get_image_properties($this->wm_overlay_path, TRUE);
1155
		$wm_img_type	= $props['image_type'];
1156
		$wm_width	= $props['width'];
1157
		$wm_height	= $props['height'];
1158
 
1159
		// Create two image resources
1160
		$wm_img  = $this->image_create_gd($this->wm_overlay_path, $wm_img_type);
1161
		$src_img = $this->image_create_gd($this->full_src_path);
1162
 
1163
		// Reverse the offset if necessary
1164
		// When the image is positioned at the bottom
1165
		// we don't want the vertical offset to push it
1166
		// further down. We want the reverse, so we'll
1167
		// invert the offset. Same with the horizontal
1168
		// offset when the image is at the right
1169
 
1170
		$this->wm_vrt_alignment = strtoupper($this->wm_vrt_alignment[0]);
1171
		$this->wm_hor_alignment = strtoupper($this->wm_hor_alignment[0]);
1172
 
1173
		if ($this->wm_vrt_alignment === 'B')
1174
			$this->wm_vrt_offset = $this->wm_vrt_offset * -1;
1175
 
1176
		if ($this->wm_hor_alignment === 'R')
1177
			$this->wm_hor_offset = $this->wm_hor_offset * -1;
1178
 
1179
		// Set the base x and y axis values
1180
		$x_axis = $this->wm_hor_offset + $this->wm_padding;
1181
		$y_axis = $this->wm_vrt_offset + $this->wm_padding;
1182
 
1183
		// Set the vertical position
1184
		if ($this->wm_vrt_alignment === 'M')
1185
		{
1186
			$y_axis += ($this->orig_height / 2) - ($wm_height / 2);
1187
		}
1188
		elseif ($this->wm_vrt_alignment === 'B')
1189
		{
1190
			$y_axis += $this->orig_height - $wm_height;
1191
		}
1192
 
1193
		// Set the horizontal position
1194
		if ($this->wm_hor_alignment === 'C')
1195
		{
1196
			$x_axis += ($this->orig_width / 2) - ($wm_width / 2);
1197
		}
1198
		elseif ($this->wm_hor_alignment === 'R')
1199
		{
1200
			$x_axis += $this->orig_width - $wm_width;
1201
		}
1202
 
1203
		// Build the finalized image
1204
		if ($wm_img_type === 3 && function_exists('imagealphablending'))
1205
		{
1206
			@imagealphablending($src_img, TRUE);
1207
		}
1208
 
1209
		// Set RGB values for text and shadow
1210
		$rgba = imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp);
1211
		$alpha = ($rgba & 0x7F000000) >> 24;
1212
 
1213
		// make a best guess as to whether we're dealing with an image with alpha transparency or no/binary transparency
1214
		if ($alpha > 0)
1215
		{
1216
			// copy the image directly, the image's alpha transparency being the sole determinant of blending
1217
			imagecopy($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height);
1218
		}
1219
		else
1220
		{
1221
			// set our RGB value from above to be transparent and merge the images with the specified opacity
1222
			imagecolortransparent($wm_img, imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp));
1223
			imagecopymerge($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height, $this->wm_opacity);
1224
		}
1225
 
1226
		// We can preserve transparency for PNG images
1227
		if ($this->image_type === 3)
1228
		{
1229
			imagealphablending($src_img, FALSE);
1230
			imagesavealpha($src_img, TRUE);
1231
		}
1232
 
1233
		// Output the image
1234
		if ($this->dynamic_output === TRUE)
1235
		{
1236
			$this->image_display_gd($src_img);
1237
		}
1238
		elseif ( ! $this->image_save_gd($src_img)) // ... or save it
1239
		{
1240
			return FALSE;
1241
		}
1242
 
1243
		imagedestroy($src_img);
1244
		imagedestroy($wm_img);
1245
 
1246
		return TRUE;
1247
	}
1248
 
1249
	// --------------------------------------------------------------------
1250
 
1251
	/**
1252
	 * Watermark - Text Version
1253
	 *
1254
	 * @return	bool
1255
	 */
1256
	public function text_watermark()
1257
	{
1258
		if ( ! ($src_img = $this->image_create_gd()))
1259
		{
1260
			return FALSE;
1261
		}
1262
 
1263
		if ($this->wm_use_truetype === TRUE && ! file_exists($this->wm_font_path))
1264
		{
1265
			$this->set_error('imglib_missing_font');
1266
			return FALSE;
1267
		}
1268
 
1269
		// Fetch source image properties
1270
		$this->get_image_properties();
1271
 
1272
		// Reverse the vertical offset
1273
		// When the image is positioned at the bottom
1274
		// we don't want the vertical offset to push it
1275
		// further down. We want the reverse, so we'll
1276
		// invert the offset. Note: The horizontal
1277
		// offset flips itself automatically
1278
 
1279
		if ($this->wm_vrt_alignment === 'B')
1280
		{
1281
			$this->wm_vrt_offset = $this->wm_vrt_offset * -1;
1282
		}
1283
 
1284
		if ($this->wm_hor_alignment === 'R')
1285
		{
1286
			$this->wm_hor_offset = $this->wm_hor_offset * -1;
1287
		}
1288
 
1289
		// Set font width and height
1290
		// These are calculated differently depending on
1291
		// whether we are using the true type font or not
1292
		if ($this->wm_use_truetype === TRUE)
1293
		{
1294
			if (empty($this->wm_font_size))
1295
			{
1296
				$this->wm_font_size = 17;
1297
			}
1298
 
1299
			if (function_exists('imagettfbbox'))
1300
			{
1301
				$temp = imagettfbbox($this->wm_font_size, 0, $this->wm_font_path, $this->wm_text);
1302
				$temp = $temp[2] - $temp[0];
1303
 
1304
				$fontwidth = $temp / strlen($this->wm_text);
1305
			}
1306
			else
1307
			{
1308
				$fontwidth = $this->wm_font_size - ($this->wm_font_size / 4);
1309
			}
1310
 
1311
			$fontheight = $this->wm_font_size;
1312
			$this->wm_vrt_offset += $this->wm_font_size;
1313
		}
1314
		else
1315
		{
1316
			$fontwidth  = imagefontwidth($this->wm_font_size);
1317
			$fontheight = imagefontheight($this->wm_font_size);
1318
		}
1319
 
1320
		// Set base X and Y axis values
1321
		$x_axis = $this->wm_hor_offset + $this->wm_padding;
1322
		$y_axis = $this->wm_vrt_offset + $this->wm_padding;
1323
 
1324
		if ($this->wm_use_drop_shadow === FALSE)
1325
		{
1326
			$this->wm_shadow_distance = 0;
1327
		}
1328
 
1329
		$this->wm_vrt_alignment = strtoupper($this->wm_vrt_alignment[0]);
1330
		$this->wm_hor_alignment = strtoupper($this->wm_hor_alignment[0]);
1331
 
1332
		// Set vertical alignment
1333
		if ($this->wm_vrt_alignment === 'M')
1334
		{
1335
			$y_axis += ($this->orig_height / 2) + ($fontheight / 2);
1336
		}
1337
		elseif ($this->wm_vrt_alignment === 'B')
1338
		{
1339
			$y_axis += $this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight / 2);
1340
		}
1341
 
1342
		// Set horizontal alignment
1343
		if ($this->wm_hor_alignment === 'R')
1344
		{
1345
			$x_axis += $this->orig_width - ($fontwidth * strlen($this->wm_text)) - $this->wm_shadow_distance;
1346
		}
1347
		elseif ($this->wm_hor_alignment === 'C')
1348
		{
1349
			$x_axis += floor(($this->orig_width - ($fontwidth * strlen($this->wm_text))) / 2);
1350
		}
1351
 
1352
		if ($this->wm_use_drop_shadow)
1353
		{
1354
			// Offset from text
1355
			$x_shad = $x_axis + $this->wm_shadow_distance;
1356
			$y_shad = $y_axis + $this->wm_shadow_distance;
1357
 
1358
			/* Set RGB values for shadow
1359
			 *
1360
			 * First character is #, so we don't really need it.
1361
			 * Get the rest of the string and split it into 2-length
1362
			 * hex values:
1363
			 */
1364
			$drp_color = str_split(substr($this->wm_shadow_color, 1, 6), 2);
1365
			$drp_color = imagecolorclosest($src_img, hexdec($drp_color[0]), hexdec($drp_color[1]), hexdec($drp_color[2]));
1366
 
1367
			// Add the shadow to the source image
1368
			if ($this->wm_use_truetype)
1369
			{
1370
				imagettftext($src_img, $this->wm_font_size, 0, $x_shad, $y_shad, $drp_color, $this->wm_font_path, $this->wm_text);
1371
			}
1372
			else
1373
			{
1374
				imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color);
1375
			}
1376
		}
1377
 
1378
		/* Set RGB values for text
1379
		 *
1380
		 * First character is #, so we don't really need it.
1381
		 * Get the rest of the string and split it into 2-length
1382
		 * hex values:
1383
		 */
1384
		$txt_color = str_split(substr($this->wm_font_color, 1, 6), 2);
1385
		$txt_color = imagecolorclosest($src_img, hexdec($txt_color[0]), hexdec($txt_color[1]), hexdec($txt_color[2]));
1386
 
1387
		// Add the text to the source image
1388
		if ($this->wm_use_truetype)
1389
		{
1390
			imagettftext($src_img, $this->wm_font_size, 0, $x_axis, $y_axis, $txt_color, $this->wm_font_path, $this->wm_text);
1391
		}
1392
		else
1393
		{
1394
			imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color);
1395
		}
1396
 
1397
		// We can preserve transparency for PNG images
1398
		if ($this->image_type === 3)
1399
		{
1400
			imagealphablending($src_img, FALSE);
1401
			imagesavealpha($src_img, TRUE);
1402
		}
1403
 
1404
		// Output the final image
1405
		if ($this->dynamic_output === TRUE)
1406
		{
1407
			$this->image_display_gd($src_img);
1408
		}
1409
		else
1410
		{
1411
			$this->image_save_gd($src_img);
1412
		}
1413
 
1414
		imagedestroy($src_img);
1415
 
1416
		return TRUE;
1417
	}
1418
 
1419
	// --------------------------------------------------------------------
1420
 
1421
	/**
1422
	 * Create Image - GD
1423
	 *
1424
	 * This simply creates an image resource handle
1425
	 * based on the type of image being processed
1426
	 *
1427
	 * @param	string
1428
	 * @param	string
1429
	 * @return	resource
1430
	 */
1431
	public function image_create_gd($path = '', $image_type = '')
1432
	{
1433
		if ($path === '')
1434
		{
1435
			$path = $this->full_src_path;
1436
		}
1437
 
1438
		if ($image_type === '')
1439
		{
1440
			$image_type = $this->image_type;
1441
		}
1442
 
1443
		switch ($image_type)
1444
		{
1445
			case 1:
1446
				if ( ! function_exists('imagecreatefromgif'))
1447
				{
1448
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
1449
					return FALSE;
1450
				}
1451
 
1452
				return imagecreatefromgif($path);
1453
			case 2:
1454
				if ( ! function_exists('imagecreatefromjpeg'))
1455
				{
1456
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
1457
					return FALSE;
1458
				}
1459
 
1460
				return imagecreatefromjpeg($path);
1461
			case 3:
1462
				if ( ! function_exists('imagecreatefrompng'))
1463
				{
1464
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
1465
					return FALSE;
1466
				}
1467
 
1468
				return imagecreatefrompng($path);
1469
			default:
1470
				$this->set_error(array('imglib_unsupported_imagecreate'));
1471
				return FALSE;
1472
		}
1473
	}
1474
 
1475
	// --------------------------------------------------------------------
1476
 
1477
	/**
1478
	 * Write image file to disk - GD
1479
	 *
1480
	 * Takes an image resource as input and writes the file
1481
	 * to the specified destination
1482
	 *
1483
	 * @param	resource
1484
	 * @return	bool
1485
	 */
1486
	public function image_save_gd($resource)
1487
	{
1488
		switch ($this->image_type)
1489
		{
1490
			case 1:
1491
				if ( ! function_exists('imagegif'))
1492
				{
1493
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
1494
					return FALSE;
1495
				}
1496
 
1497
				if ( ! @imagegif($resource, $this->full_dst_path))
1498
				{
1499
					$this->set_error('imglib_save_failed');
1500
					return FALSE;
1501
				}
1502
			break;
1503
			case 2:
1504
				if ( ! function_exists('imagejpeg'))
1505
				{
1506
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
1507
					return FALSE;
1508
				}
1509
 
1510
				if ( ! @imagejpeg($resource, $this->full_dst_path, $this->quality))
1511
				{
1512
					$this->set_error('imglib_save_failed');
1513
					return FALSE;
1514
				}
1515
			break;
1516
			case 3:
1517
				if ( ! function_exists('imagepng'))
1518
				{
1519
					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
1520
					return FALSE;
1521
				}
1522
 
1523
				if ( ! @imagepng($resource, $this->full_dst_path))
1524
				{
1525
					$this->set_error('imglib_save_failed');
1526
					return FALSE;
1527
				}
1528
			break;
1529
			default:
1530
				$this->set_error(array('imglib_unsupported_imagecreate'));
1531
				return FALSE;
1532
			break;
1533
		}
1534
 
1535
		return TRUE;
1536
	}
1537
 
1538
	// --------------------------------------------------------------------
1539
 
1540
	/**
1541
	 * Dynamically outputs an image
1542
	 *
1543
	 * @param	resource
1544
	 * @return	void
1545
	 */
1546
	public function image_display_gd($resource)
1547
	{
1548
		header('Content-Disposition: filename='.$this->source_image.';');
1549
		header('Content-Type: '.$this->mime_type);
1550
		header('Content-Transfer-Encoding: binary');
1551
		header('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT');
1552
 
1553
		switch ($this->image_type)
1554
		{
1555
			case 1	:	imagegif($resource);
1556
				break;
1557
			case 2	:	imagejpeg($resource, NULL, $this->quality);
1558
				break;
1559
			case 3	:	imagepng($resource);
1560
				break;
1561
			default:	echo 'Unable to display the image';
1562
				break;
1563
		}
1564
	}
1565
 
1566
	// --------------------------------------------------------------------
1567
 
1568
	/**
1569
	 * Re-proportion Image Width/Height
1570
	 *
1571
	 * When creating thumbs, the desired width/height
1572
	 * can end up warping the image due to an incorrect
1573
	 * ratio between the full-sized image and the thumb.
1574
	 *
1575
	 * This function lets us re-proportion the width/height
1576
	 * if users choose to maintain the aspect ratio when resizing.
1577
	 *
1578
	 * @return	void
1579
	 */
1580
	public function image_reproportion()
1581
	{
1582
		if (($this->width === 0 && $this->height === 0) OR $this->orig_width === 0 OR $this->orig_height === 0
1583
			OR ( ! ctype_digit((string) $this->width) && ! ctype_digit((string) $this->height))
1584
			OR ! ctype_digit((string) $this->orig_width) OR ! ctype_digit((string) $this->orig_height))
1585
		{
1586
			return;
1587
		}
1588
 
1589
		// Sanitize
1590
		$this->width = (int) $this->width;
1591
		$this->height = (int) $this->height;
1592
 
1593
		if ($this->master_dim !== 'width' && $this->master_dim !== 'height')
1594
		{
1595
			if ($this->width > 0 && $this->height > 0)
1596
			{
1597
				$this->master_dim = ((($this->orig_height/$this->orig_width) - ($this->height/$this->width)) < 0)
1598
							? 'width' : 'height';
1599
			}
1600
			else
1601
			{
1602
				$this->master_dim = ($this->height === 0) ? 'width' : 'height';
1603
			}
1604
		}
1605
		elseif (($this->master_dim === 'width' && $this->width === 0)
1606
			OR ($this->master_dim === 'height' && $this->height === 0))
1607
		{
1608
			return;
1609
		}
1610
 
1611
		if ($this->master_dim === 'width')
1612
		{
1613
			$this->height = (int) ceil($this->width*$this->orig_height/$this->orig_width);
1614
		}
1615
		else
1616
		{
1617
			$this->width = (int) ceil($this->orig_width*$this->height/$this->orig_height);
1618
		}
1619
	}
1620
 
1621
	// --------------------------------------------------------------------
1622
 
1623
	/**
1624
	 * Get image properties
1625
	 *
1626
	 * A helper function that gets info about the file
1627
	 *
1628
	 * @param	string
1629
	 * @param	bool
1630
	 * @return	mixed
1631
	 */
1632
	public function get_image_properties($path = '', $return = FALSE)
1633
	{
1634
		// For now we require GD but we should
1635
		// find a way to determine this using IM or NetPBM
1636
 
1637
		if ($path === '')
1638
		{
1639
			$path = $this->full_src_path;
1640
		}
1641
 
1642
		if ( ! file_exists($path))
1643
		{
1644
			$this->set_error('imglib_invalid_path');
1645
			return FALSE;
1646
		}
1647
 
1648
		$vals = getimagesize($path);
1649
		$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
1650
		$mime = (isset($types[$vals[2]])) ? 'image/'.$types[$vals[2]] : 'image/jpg';
1651
 
1652
		if ($return === TRUE)
1653
		{
1654
			return array(
1655
					'width' =>	$vals[0],
1656
					'height' =>	$vals[1],
1657
					'image_type' =>	$vals[2],
1658
					'size_str' =>	$vals[3],
1659
					'mime_type' =>	$mime
1660
				);
1661
		}
1662
 
1663
		$this->orig_width	= $vals[0];
1664
		$this->orig_height	= $vals[1];
1665
		$this->image_type	= $vals[2];
1666
		$this->size_str		= $vals[3];
1667
		$this->mime_type	= $mime;
1668
 
1669
		return TRUE;
1670
	}
1671
 
1672
	// --------------------------------------------------------------------
1673
 
1674
	/**
1675
	 * Size calculator
1676
	 *
1677
	 * This function takes a known width x height and
1678
	 * recalculates it to a new size. Only one
1679
	 * new variable needs to be known
1680
	 *
1681
	 *	$props = array(
1682
	 *			'width'		=> $width,
1683
	 *			'height'	=> $height,
1684
	 *			'new_width'	=> 40,
1685
	 *			'new_height'	=> ''
1686
	 *		);
1687
	 *
1688
	 * @param	array
1689
	 * @return	array
1690
	 */
1691
	public function size_calculator($vals)
1692
	{
1693
		if ( ! is_array($vals))
1694
		{
1695
			return;
1696
		}
1697
 
1698
		$allowed = array('new_width', 'new_height', 'width', 'height');
1699
 
1700
		foreach ($allowed as $item)
1701
		{
1702
			if (empty($vals[$item]))
1703
			{
1704
				$vals[$item] = 0;
1705
			}
1706
		}
1707
 
1708
		if ($vals['width'] === 0 OR $vals['height'] === 0)
1709
		{
1710
			return $vals;
1711
		}
1712
 
1713
		if ($vals['new_width'] === 0)
1714
		{
1715
			$vals['new_width'] = ceil($vals['width']*$vals['new_height']/$vals['height']);
1716
		}
1717
		elseif ($vals['new_height'] === 0)
1718
		{
1719
			$vals['new_height'] = ceil($vals['new_width']*$vals['height']/$vals['width']);
1720
		}
1721
 
1722
		return $vals;
1723
	}
1724
 
1725
	// --------------------------------------------------------------------
1726
 
1727
	/**
1728
	 * Explode source_image
1729
	 *
1730
	 * This is a helper function that extracts the extension
1731
	 * from the source_image.  This function lets us deal with
1732
	 * source_images with multiple periods, like: my.cool.jpg
1733
	 * It returns an associative array with two elements:
1734
	 * $array['ext']  = '.jpg';
1735
	 * $array['name'] = 'my.cool';
1736
	 *
1737
	 * @param	array
1738
	 * @return	array
1739
	 */
1740
	public function explode_name($source_image)
1741
	{
1742
		$ext = strrchr($source_image, '.');
1743
		$name = ($ext === FALSE) ? $source_image : substr($source_image, 0, -strlen($ext));
1744
 
1745
		return array('ext' => $ext, 'name' => $name);
1746
	}
1747
 
1748
	// --------------------------------------------------------------------
1749
 
1750
	/**
1751
	 * Is GD Installed?
1752
	 *
1753
	 * @return	bool
1754
	 */
1755
	public function gd_loaded()
1756
	{
1757
		if ( ! extension_loaded('gd'))
1758
		{
1759
			/* As it is stated in the PHP manual, dl() is not always available
1760
			 * and even if so - it could generate an E_WARNING message on failure
1761
			 */
1762
			return (function_exists('dl') && @dl('gd.so'));
1763
		}
1764
 
1765
		return TRUE;
1766
	}
1767
 
1768
	// --------------------------------------------------------------------
1769
 
1770
	/**
1771
	 * Get GD version
1772
	 *
1773
	 * @return	mixed
1774
	 */
1775
	public function gd_version()
1776
	{
1777
		if (function_exists('gd_info'))
1778
		{
1779
			$gd_version = @gd_info();
1780
			return preg_replace('/\D/', '', $gd_version['GD Version']);
1781
		}
1782
 
1783
		return FALSE;
1784
	}
1785
 
1786
	// --------------------------------------------------------------------
1787
 
1788
	/**
1789
	 * Set error message
1790
	 *
1791
	 * @param	string
1792
	 * @return	void
1793
	 */
1794
	public function set_error($msg)
1795
	{
1796
		$CI =& get_instance();
1797
		$CI->lang->load('imglib');
1798
 
1799
		if (is_array($msg))
1800
		{
1801
			foreach ($msg as $val)
1802
			{
1803
				$msg = ($CI->lang->line($val) === FALSE) ? $val : $CI->lang->line($val);
1804
				$this->error_msg[] = $msg;
1805
				log_message('error', $msg);
1806
			}
1807
		}
1808
		else
1809
		{
1810
			$msg = ($CI->lang->line($msg) === FALSE) ? $msg : $CI->lang->line($msg);
1811
			$this->error_msg[] = $msg;
1812
			log_message('error', $msg);
1813
		}
1814
	}
1815
 
1816
	// --------------------------------------------------------------------
1817
 
1818
	/**
1819
	 * Show error messages
1820
	 *
1821
	 * @param	string
1822
	 * @param	string
1823
	 * @return	string
1824
	 */
1825
	public function display_errors($open = '<p>', $close = '</p>')
1826
	{
1827
		return (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : '';
1828
	}
1829
 
1830
}