Subversion Repositories cheapmusic

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
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
}