98 |
- |
1 |
<?php
|
|
|
2 |
/* $Id: data_table.php 1000 2011-08-06 01:19:27Z lbayuk $
|
|
|
3 |
phplot / contrib / data_table.php: Draw a table of data values
|
|
|
4 |
|
|
|
5 |
Copyright (c) 2011, lbayuk -at- users.sourceforge.net
|
|
|
6 |
All rights reserved.
|
|
|
7 |
|
|
|
8 |
Redistribution and use in source and binary forms, with or without
|
|
|
9 |
modification, are permitted provided that the following conditions are met:
|
|
|
10 |
|
|
|
11 |
* Redistributions of source code must retain the above copyright
|
|
|
12 |
notice, this list of conditions and the following disclaimer.
|
|
|
13 |
* Redistributions in binary form must reproduce the above copyright
|
|
|
14 |
notice, this list of conditions and the following disclaimer in the
|
|
|
15 |
documentation and/or other materials provided with the distribution.
|
|
|
16 |
|
|
|
17 |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
18 |
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
19 |
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
20 |
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
21 |
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
22 |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
23 |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
24 |
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
25 |
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
26 |
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
27 |
POSSIBILITY OF SUCH DAMAGE.
|
|
|
28 |
|
|
|
29 |
This function draws a data table. That is, using PHP GD functions, it draws
|
|
|
30 |
a table containing elements from a 2-dimensional array of text and number data.
|
|
|
31 |
|
|
|
32 |
This is meant to be used as a PHPlot callback, but does not use PHPlot.
|
|
|
33 |
It takes 2 arguments: a PHP GD image resource, and an array of settings. It
|
|
|
34 |
draws the table into the image and returns TRUE. On error, it uses
|
|
|
35 |
trigger_error(); if an error handler returns, it returns FALSE.
|
|
|
36 |
|
|
|
37 |
The following keys are required in the settings array:
|
|
|
38 |
|
|
|
39 |
'data' => A PHPlot-style data array. Array[0..n-1] of rows, each
|
|
|
40 |
row entry is an array with values (strings or numbers). The
|
|
|
41 |
'headers' entry (see below) determines which columns are used in
|
|
|
42 |
the table. Other settings control how the values are formatted and
|
|
|
43 |
drawn in the table.
|
|
|
44 |
Note: Multi-line string values (with newlines) do not work.
|
|
|
45 |
|
|
|
46 |
'headers' => Array of headers for the data array.
|
|
|
47 |
There must be an entry for each data array column. The entry is the
|
|
|
48 |
header label for that column. NULL means do not include this data array
|
|
|
49 |
column in the table.
|
|
|
50 |
Header labels are centered within the header row of the table.
|
|
|
51 |
Multi-line entries (with newlines) are not supported and do not work.
|
|
|
52 |
|
|
|
53 |
The following keys are optional in the settings array:
|
|
|
54 |
|
|
|
55 |
'position' => Array (x, y) giving the GD coordinates for the upper left
|
|
|
56 |
corner of the table. The default is (0,0), which is the upper left
|
|
|
57 |
corner of the image.
|
|
|
58 |
|
|
|
59 |
'height' => Height in pixels of the table.
|
|
|
60 |
If not given or empty or 0, the table height is calculated as the minimum
|
|
|
61 |
value, taking into account the number of rows, font height, and
|
|
|
62 |
cellpadding value.
|
|
|
63 |
|
|
|
64 |
'width' => Width in pixels of the table
|
|
|
65 |
If not given or empty or 0, the table width is calculated to just fit
|
|
|
66 |
the widest value in each column (after formatting).
|
|
|
67 |
If width is not provided, column_widths is ignored.
|
|
|
68 |
|
|
|
69 |
'column_widths' => Array of relative column width weights.
|
|
|
70 |
For example: array(4,2,1,1) means the 1st column is 2 times
|
|
|
71 |
as wide as the 2nd, and 4 times as wide as the 3rd and 4th.
|
|
|
72 |
If missing, all columns get equal width. Ignored if the overall
|
|
|
73 |
table width is not given.
|
|
|
74 |
|
|
|
75 |
'column_formats' => Array of printf formats for each column.
|
|
|
76 |
An empty string means no formatting for that column - the value is
|
|
|
77 |
drawn into the table using PHP's defaults.
|
|
|
78 |
If missing, there is no formatting for any column.
|
|
|
79 |
|
|
|
80 |
'column_alignments' => Array of L C or R for horizontal alignment, meaning
|
|
|
81 |
left align, center, or right align.
|
|
|
82 |
An empty string means automatic: align right if numeric, else left.
|
|
|
83 |
Default if missing is automatic for all columns.
|
|
|
84 |
|
|
|
85 |
'font' => GD font number 1-5. 1 is smallest, 5 is largest. Default is 2.
|
|
|
86 |
|
|
|
87 |
'color' => Array with R, G, B color specification. Default is (0,0,0)=black.
|
|
|
88 |
White would be (255,255,255). This color is used for grid lines
|
|
|
89 |
and text in the table.
|
|
|
90 |
|
|
|
91 |
'cellpadding' => Line to text gap in pixels. Default is 4.
|
|
|
92 |
|
|
|
93 |
Note that the arrays 'column_widths', 'column_formats', and 'column_alignments'
|
|
|
94 |
have one entry for each column in the table which will be drawn. This is
|
|
|
95 |
different from 'headers', which has one column for each column in the
|
|
|
96 |
data array. These sizes will be the same only if 'headers' has no NULL
|
|
|
97 |
entries - that is, no data array columns are being skipped.
|
|
|
98 |
|
|
|
99 |
*/
|
|
|
100 |
function draw_data_table($img, $settings)
|
|
|
101 |
{
|
|
|
102 |
// Apply defaults, then extract all settings as variables named 'o_*':
|
|
|
103 |
extract(array_merge(array(
|
|
|
104 |
'color' => array(0, 0, 0), // Default black
|
|
|
105 |
'font' => 2, // Default GD font
|
|
|
106 |
'position' => array(0, 0), // Default to upper left corner
|
|
|
107 |
'width' => 0, // Default auto width calculation
|
|
|
108 |
'height' => 0, // Default auto height calculation
|
|
|
109 |
'cellpadding' => 4, // Default line-to-text spacing
|
|
|
110 |
), $settings), EXTR_PREFIX_ALL, 'o');
|
|
|
111 |
list($x, $y) = $o_position; // Expand to separate variables
|
|
|
112 |
|
|
|
113 |
// Check for mandatory settings:
|
|
|
114 |
if (!isset($o_data, $o_headers)) {
|
|
|
115 |
trigger_error("draw_data_table error: 'headers' and 'data' are required");
|
|
|
116 |
return FALSE; // In case error handler returns
|
|
|
117 |
}
|
|
|
118 |
|
|
|
119 |
// Font and color setup:
|
|
|
120 |
$char_width = imagefontwidth($o_font);
|
|
|
121 |
$char_height = imagefontheight($o_font);
|
|
|
122 |
$color = imagecolorresolve($img, $o_color[0], $o_color[1], $o_color[2]);
|
|
|
123 |
$pad2 = 2 * $o_cellpadding; // Pad all 4 sides of cells
|
|
|
124 |
|
|
|
125 |
// Calculate the number of rows and columns in the table:
|
|
|
126 |
$n_rows = count($o_data) + 1; // Add 1 for header row
|
|
|
127 |
// Count non-skipped columns:
|
|
|
128 |
$n_cols = 0;
|
|
|
129 |
foreach ($o_headers as $h) if (!is_null($h)) $n_cols++;
|
|
|
130 |
// Number of columns in the data array and in $o_headers:
|
|
|
131 |
$n_data_cols = count($o_headers);
|
|
|
132 |
|
|
|
133 |
// Default column weights so all columns have equal width.
|
|
|
134 |
if (empty($o_column_widths))
|
|
|
135 |
$o_column_widths = array_fill(0, $n_cols, 1);
|
|
|
136 |
|
|
|
137 |
// Default column formats to no formatting:
|
|
|
138 |
if (empty($o_column_formats))
|
|
|
139 |
$o_column_formats = array_fill(0, $n_cols, '');
|
|
|
140 |
|
|
|
141 |
// Default column alignments to auto align:
|
|
|
142 |
if (empty($o_column_alignments))
|
|
|
143 |
$o_column_alignments = array_fill(0, $n_cols, '');
|
|
|
144 |
|
|
|
145 |
// Make sure there are the right number of entries.
|
|
|
146 |
if (count($o_column_widths) != $n_cols
|
|
|
147 |
|| count($o_column_formats) != $n_cols
|
|
|
148 |
|| count($o_column_alignments) != $n_cols) {
|
|
|
149 |
trigger_error("draw_data_table error: Mismatch in size of column spec arrays");
|
|
|
150 |
return FALSE; // In case error handler returns
|
|
|
151 |
}
|
|
|
152 |
|
|
|
153 |
// If the table height is not supplied, calculate the space needed.
|
|
|
154 |
if (empty($o_height))
|
|
|
155 |
$o_height = $n_rows * ($char_height + $pad2);
|
|
|
156 |
// Then calculate the height of each row.
|
|
|
157 |
$row_height = $o_height / $n_rows;
|
|
|
158 |
|
|
|
159 |
// If the table width is not supplied, calculate the space needed based
|
|
|
160 |
// on the widest value for each column (including header). The column width
|
|
|
161 |
// factors are ignored, since each column will be as wide as needed.
|
|
|
162 |
if (empty($o_width)) {
|
|
|
163 |
$o_width = 0;
|
|
|
164 |
$col = 0; // Index to unskipped columns
|
|
|
165 |
for ($i = 0; $i < $n_data_cols; $i++) { // Index to all columns
|
|
|
166 |
if (is_null($o_headers[$i])) continue; // Skip column
|
|
|
167 |
// Find the longest string in this column, post-formatting.
|
|
|
168 |
$len = strlen($o_headers[$i]); // Start with the header
|
|
|
169 |
for ($row = 1; $row < $n_rows; $row++) {
|
|
|
170 |
if (($cell = $o_data[$row - 1][$i]) !== '') { // Non-empty
|
|
|
171 |
// Apply cell format if specified:
|
|
|
172 |
if (($fmt = $o_column_formats[$col]) != '')
|
|
|
173 |
$cell = sprintf($fmt, $cell);
|
|
|
174 |
if (($this_len = strlen($cell)) > $len)
|
|
|
175 |
$len = $this_len;
|
|
|
176 |
}
|
|
|
177 |
}
|
|
|
178 |
// Assign column width, and accumulate the total:
|
|
|
179 |
$o_width += $col_width[$col++] = $len * $char_width + $pad2;
|
|
|
180 |
}
|
|
|
181 |
} else { // Table width, and optionally column width factors, are supplied.
|
|
|
182 |
// Calculate the width of each column.
|
|
|
183 |
$col_width_scale = $o_width / array_sum($o_column_widths);
|
|
|
184 |
for ($col = 0; $col < $n_cols; $col++)
|
|
|
185 |
$col_width[$col] = $o_column_widths[$col] * $col_width_scale;
|
|
|
186 |
}
|
|
|
187 |
|
|
|
188 |
// Calculate the column start positions within the table:
|
|
|
189 |
$col_start[0] = 0;
|
|
|
190 |
for ($i = 1; $i < $n_cols; $i++)
|
|
|
191 |
$col_start[$i] = $col_start[$i-1] + $col_width[$i-1];
|
|
|
192 |
|
|
|
193 |
// Draw the table grid (without outer border)
|
|
|
194 |
$x2 = $x + $o_width - 1;
|
|
|
195 |
for ($row = 1; $row < $n_rows; $row++) {
|
|
|
196 |
$y0 = $y + $row_height * $row; // Avoid accumulating errors.
|
|
|
197 |
imageline($img, $x, $y0, $x2, $y0, $color);
|
|
|
198 |
}
|
|
|
199 |
$y2 = $y + $o_height - 1;
|
|
|
200 |
for ($col = 1; $col < $n_cols; $col++) {
|
|
|
201 |
$x0 = $x + $col_start[$col];
|
|
|
202 |
imageline($img, $x0, $y, $x0, $y2, $color);
|
|
|
203 |
}
|
|
|
204 |
|
|
|
205 |
// Draw the header row, then the data rows
|
|
|
206 |
for ($row = 0; $row < $n_rows; $row++) {
|
|
|
207 |
|
|
|
208 |
// Vertically center the cell contents within the cell:
|
|
|
209 |
$y0 = $y + $row_height * ($row + 0.5) - $char_height / 2;
|
|
|
210 |
|
|
|
211 |
if ($row == 0) $cells = $o_headers; // Header row
|
|
|
212 |
else $cells = $o_data[$row - 1]; // -1 accounts for header row.
|
|
|
213 |
|
|
|
214 |
$col = 0; // Index to unskipped columns
|
|
|
215 |
for ($i = 0; $i < $n_data_cols; $i++) { // Index to all columns
|
|
|
216 |
|
|
|
217 |
if (is_null($o_headers[$i])) continue; // NULL header => skip column
|
|
|
218 |
|
|
|
219 |
if (($cell = $cells[$i]) !== '') { // Empty cell?
|
|
|
220 |
if ($row == 0) {
|
|
|
221 |
$alg = 'C'; // Header row forces center alignment
|
|
|
222 |
} else {
|
|
|
223 |
// Apply cell format if specified:
|
|
|
224 |
if (($fmt = $o_column_formats[$col]) != '')
|
|
|
225 |
$cell = sprintf($fmt, $cell);
|
|
|
226 |
// Get cell alignment:
|
|
|
227 |
$alg = $o_column_alignments[$col];
|
|
|
228 |
}
|
|
|
229 |
|
|
|
230 |
// Calculate upper left position for this cell's text:
|
|
|
231 |
if (empty($alg)) // Default alignment: numbers right, else left.
|
|
|
232 |
$alg = is_numeric($cell) ? 'R' : 'L';
|
|
|
233 |
$x0 = $x + $col_start[$col];
|
|
|
234 |
if ($alg == 'R') {
|
|
|
235 |
$x0 += $col_width[$col] - strlen($cell) * $char_width - $o_cellpadding;
|
|
|
236 |
} elseif ($alg == 'C') {
|
|
|
237 |
$x0 += ($col_width[$col] - strlen($cell) * $char_width) / 2;
|
|
|
238 |
} else { // Default, assume L
|
|
|
239 |
$x0 += $o_cellpadding;
|
|
|
240 |
}
|
|
|
241 |
imagestring($img, $o_font, $x0, $y0, $cell, $color);
|
|
|
242 |
}
|
|
|
243 |
$col++;
|
|
|
244 |
}
|
|
|
245 |
}
|
|
|
246 |
return TRUE;
|
|
|
247 |
}
|