1: <?php
2:
3: /**
4: * Abstract class that is designed to be extended for drawing pie charts with
5: * different graphics libraries. Use PieChartGD or PieChartImagick to actually
6: * draw your charts.
7: * @author Sam Christy <sam_christy@hotmail.co.uk>
8: * @licence GNU GPL v3.0 <http://www.gnu.org/licenses/gpl-3.0.html>
9: * @copyright © Sam Christy 2013
10: * @package PieChart
11: * @version v1.2
12: */
13: abstract class PieChart {
14: const FORMAT_GIF = 1;
15: const FORMAT_JPEG = 2;
16: const FORMAT_PNG = 3;
17: const OUTPUT_DOWNLOAD = 1;
18: const OUTPUT_INLINE = 2;
19: const OUTPUT_SAVE = 3;
20:
21: protected $slices;
22: protected $width;
23: protected $height;
24: protected $title;
25: protected $hasLegend;
26: protected $titleFont;
27: protected $legendFont;
28: protected $textColor;
29: protected $backgroundColor;
30: protected $canvas;
31: protected $quality;
32:
33: /**
34: * Constructs the PieChart.
35: * @param int $width The width of the chart, in pixels.
36: * @param int $height The chart's height, in pixels.
37: * @param string [$title] The chart's title.
38: * @param string|int|array [$textColor] The colour of the title and labels.
39: * @param string|int|array [$backgroundColor] The color for the background.
40: */
41: public function __construct($width = 0, $height = 0, $title = '', $textColor = 0x222222,
42: $backgroundColor = 0xffffff) {
43: $this->width = $width;
44: $this->height = $height;
45: $this->title = $title;
46: $this->hasLegend = true;
47: $this->slices = array();
48: $this->quality = 100;
49: $this->textColor = new PieChartColor($textColor);
50: $this->backgroundColor = new PieChartColor($backgroundColor);
51: $this->titleFont = __DIR__ . '/fonts/Open_Sans/OpenSans-Semibold.ttf';
52: $this->legendFont = __DIR__ . '/fonts/Open_Sans/OpenSans-Regular.ttf';
53: }
54:
55: /**
56: * Frees the memory that was allocated to the image. Use this function to
57: * clean up after your pie chart when you're finished with it.
58: */
59: public function destroy() {}
60:
61: /**
62: * Sets the title's text. To remove the title, set it to ''.
63: * @param string $title
64: * @param string [$titleFont] The name of the font file for the title.
65: */
66: public function setTitle($title, $titleFont = NULL) {
67: $this->title = $title;
68:
69: if($titleFont)
70: $this->titleFont = $titleFont;
71: }
72:
73: /**
74: * Add or remove the chart's legend (it is displayed default).
75: * @param bool $displayLegend Specify false to remove the legend or true to
76: * add one.
77: * @param string [$legendFont] The name of the font for the legend's text.
78: */
79: public function setLegend($displayLegend, $legendFont = NULL) {
80: $this->hasLegend = $displayLegend;
81:
82: if($legendFont)
83: $this->legendFont = $legendFont;
84: }
85:
86: /**
87: * Set the quality for generating output in lossy formats.
88: * @param int $quality An integer between 0 and 100 (inclusive).
89: */
90: public function setOutputQuality($quality) {
91: $this->quality = $quality;
92: }
93:
94: /**
95: * Adds a new slice to the pie chart.
96: * @param string $name The name of the slice (used for legend label).
97: * @param float $value
98: * @param string|int|array $color The CSS colour, e.g. '#FFFFFF', 'rgb(255, 255, 255)'.
99: */
100: public function addSlice($name, $value, $color) {
101: $this->slices[$name] = array(
102: 'value' => $value,
103: 'color' => new PieChartColor($color)
104: );
105: }
106:
107: /**
108: * Removes the specified slice.
109: * @param string $name The name of the slice to be removed.
110: */
111: public function removeSlice($name) {
112: unset($this->slices[$name]);
113: }
114:
115: /**
116: * Draws the chart so that it is ready for output.
117: */
118: public function draw() {}
119:
120: /**
121: * For child classes to override, so that the output functions work.
122: * @param int $method
123: * @param int $format
124: * @param string $filename
125: */
126: protected function _output($method, $format, $filename) {}
127:
128: /**
129: * Echos the chart as a GIF and instructs the browser to display it inline.
130: * @param string [$filename] The filename for the picture.
131: * @return bool true if successful, false otherwise (implementation-dependent).
132: */
133: public function outputGIF($filename = 'pie-chart.gif') {
134: header('Content-Type: image/gif');
135: header("Content-Disposition: inline; filename=\"$filename\"");
136:
137: return $this->_output(self::OUTPUT_INLINE, self::FORMAT_GIF, $filename);
138: }
139:
140: /**
141: * Echos the chart as a JPEG and instructs the browser to display it inline.
142: * @param string [$filename] The filename for the picture.
143: * @return bool true if successful, false otherwise (implementation-dependent).
144: */
145: public function outputJPEG($filename = 'pie-chart.jpg') {
146: header('Content-Type: image/jpeg');
147: header("Content-Disposition: inline; filename=\"$filename\"");
148:
149: return $this->_output(self::OUTPUT_INLINE, self::FORMAT_JPEG, $filename);
150: }
151:
152: /**
153: * Echos the chart as a PNG and instructs the browser to display it inline.
154: * @param string [$filename] The filename for the picture.
155: * @return bool true if successful, false otherwise (implementation-dependent).
156: */
157: public function outputPNG($filename = 'pie-chart.png') {
158: header('Content-Type: image/png');
159: header("Content-Disposition: inline; filename=\"$filename\"");
160:
161: return $this->_output(self::OUTPUT_INLINE, self::FORMAT_PNG, $filename);
162: }
163:
164: /**
165: * Echos the chart as a GIF and instructs the browser to force the user to
166: * save it.
167: * @param string [$filename] The filename for the picture.
168: * @return bool true if successful, false otherwise (implementation-dependent).
169: */
170: public function forceDownloadGIF($filename = 'pie-chart.gif') {
171: header("Pragma: public");
172: header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
173: header('Content-Type: application/octet-stream');
174: header('Content-Disposition: attachment; filename="'.$filename.'"');
175:
176: return $this->_output(self::OUTPUT_INLINE, self::FORMAT_GIF, $filename);
177: }
178:
179: /**
180: * Echos the chart as a JPEG and instructs the browser to force the user to
181: * save it.
182: * @param string [$filename] The filename for the picture.
183: * @return bool true if successful, false otherwise (implementation-dependent).
184: */
185: public function forceDownloadJPEG($filename = 'pie-chart.jpg') {
186: header("Pragma: public");
187: header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
188: header('Content-Type: application/octet-stream');
189: header('Content-Disposition: attachment; filename="'.$filename.'"');
190:
191: return $this->_output(self::OUTPUT_INLINE, self::FORMAT_JPEG, $filename);
192: }
193:
194: /**
195: * Echos the chart as a PNG and instructs the browser to force the user to
196: * save it.
197: * @param string [$filename] The filename for the picture.
198: * @return bool true if successful, false otherwise (implementation-dependent).
199: */
200: public function forceDownloadPNG($filename = 'pie-chart.png') {
201: header("Pragma: public");
202: header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
203: header('Content-Type: application/octet-stream');
204: header('Content-Disposition: attachment; filename="'.$filename.'"');
205:
206: return $this->_output(self::OUTPUT_DOWNLOAD, self::FORMAT_PNG, $filename);
207: }
208:
209: /**
210: * Saves the chart as a GIF, in the specified location.
211: * @param string $filename
212: * @return int true if successful, false otherwise (implementation-dependent)..
213: */
214: public function saveGIF($filename) {
215: return $this->_output(self::OUTPUT_SAVE, self::FORMAT_GIF, $filename);
216: }
217:
218: /**
219: * Saves the chart as a JPEG, in the specified location.
220: * @param string $filename
221: * @return int true if successful, false otherwise (implementation-dependent).
222: */
223: public function saveJPEG($filename) {
224: return $this->_output(self::OUTPUT_SAVE, self::FORMAT_JPEG, $filename);
225: }
226:
227: /**
228: * Saves the chart as a PNG, in the specified location.
229: * @param string $filename
230: * @return int true if successful, false otherwise (implementation-dependent).
231: */
232: public function savePNG($filename) {
233: return $this->_output(self::OUTPUT_SAVE, self::FORMAT_PNG, $filename);
234: }
235: }
236:
237: /**
238: * Utility class for storing colours in a library-agnostic format.
239: * @package PieChart
240: */
241: class PieChartColor {
242: public $r;
243: public $g;
244: public $b;
245:
246: /**
247: * Sets the colour using {@link setColor()}, if an argument is provided.
248: * @param array|int|string [$color]
249: */
250: public function __construct($color = NULL) {
251: if (!is_null($color)) {
252: $this->setColor($color);
253: }
254: }
255:
256: /**
257: * Sets the colour, using one of the following formats: '#FFFFFF', '#fff',
258: * 'rgb(255, 255, 255)', [$r, $g, $b] or ARGB
259: * ({@link http://en.wikipedia.org/wiki/ARGB#ARGB}).
260: * @param array|int|string $color
261: */
262: public function setColor($color) {
263:
264: switch (getType($color)) {
265: case 'array':
266: $this->r = $color[0];
267: $this->g = $color[1];
268: $this->b = $color[2];
269: break;
270:
271: case 'integer': // ARGB format
272: $this->r = $color >> 16 & 0xFF;
273: $this->g = $color >> 8 & 0xFF;
274: $this->b = $color & 0xFF;
275: break;
276:
277: case 'string':
278: $length = strLen($color);
279:
280: if ($length == 7) {
281: // e.g. '#FFFFFF'.
282: $this->r = hexDec(subStr($color, 1, 2));
283: $this->g = hexDec(subStr($color, 3, 2));
284: $this->b = hexDec(subStr($color, 5, 2));
285:
286: } else if ($length == 4) {
287: // e.g. '#FFF'.
288: $this->r = hexDec(subStr($color, 1, 1)) * 17;
289: $this->g = hexDec(subStr($color, 2, 1)) * 17;
290: $this->b = hexDec(subStr($color, 3, 1)) * 17;
291:
292: } else if (strToLower(subStr($color, 0, 4)) == 'rgb(') {
293: // e.g. 'rgb(255, 255, 255)'.
294: $listOfColors = subStr($color, 4, -1);
295: $arrayOfColors = explode(',', $listOfColors);
296:
297: $this->r = intVal($arrayOfColors[0]);
298: $this->g = intVal($arrayOfColors[1]);
299: $this->b = intVal($arrayOfColors[2]);
300: }
301: break;
302: }
303: }
304:
305: /**
306: * Returns the colour as an integer, in the ARGB format
307: * ({@link http://en.wikipedia.org/wiki/ARGB#ARGB}).
308: * @return int
309: */
310: public function toInt() {
311: $color = 0;
312:
313: $color |= $this->r << 16;
314: $color |= $this->g << 8;
315: $color |= $this->b;
316:
317: return $color;
318: }
319:
320: /**
321: * Returns the colour in the RGB string format, e.g. 'rgb(0,0,0)'.
322: * @return string
323: */
324: public function toRGB() {
325: return 'rgb(' . $this->r . ',' . $this->g . ',' . $this->b . ')';
326: }
327:
328: /**
329: * Returns the color in the CSS hexadecimal format, e.g. '#000000'.
330: * @return string
331: */
332: public function toHex() {
333: return sprintf('#%02s%02s%02s', decHex($this->r), decHex($this->g), decHex($this->b));
334: }
335: }