Annotation of /trunk/lib/iptc/JFIF.php
Parent Directory
|
Revision Log
Revision 2 - (view) (download)
| 1 : | andphe | 2 | <?php |
| 2 : | /** | ||
| 3 : | * @package zOOmGallery | ||
| 4 : | * @author Mike de Boer <mailme@mikedeboer.nl> | ||
| 5 : | **/ | ||
| 6 : | /****************************************************************************** | ||
| 7 : | * | ||
| 8 : | * Filename: JFIF.php | ||
| 9 : | * | ||
| 10 : | * Description: Provides functions for reading and writing information to/from | ||
| 11 : | * JPEG File Interchange Format (JFIF) segments and | ||
| 12 : | * JFIF Extension (JFXX) segments within a JPEG file. | ||
| 13 : | * | ||
| 14 : | * Author: Evan Hunter | ||
| 15 : | * | ||
| 16 : | * Date: 24/7/2004 | ||
| 17 : | * | ||
| 18 : | * Project: PHP JPEG Metadata Toolkit | ||
| 19 : | * | ||
| 20 : | * Revision: 1.00 | ||
| 21 : | * | ||
| 22 : | * URL: http://electronics.ozhiker.com | ||
| 23 : | * | ||
| 24 : | * Copyright: Copyright Evan Hunter 2004 | ||
| 25 : | * | ||
| 26 : | * License: This file is part of the PHP JPEG Metadata Toolkit. | ||
| 27 : | * | ||
| 28 : | * The PHP JPEG Metadata Toolkit is free software; you can | ||
| 29 : | * redistribute it and/or modify it under the terms of the | ||
| 30 : | * GNU General Public License as published by the Free Software | ||
| 31 : | * Foundation; either version 2 of the License, or (at your | ||
| 32 : | * option) any later version. | ||
| 33 : | * | ||
| 34 : | * The PHP JPEG Metadata Toolkit is distributed in the hope | ||
| 35 : | * that it will be useful, but WITHOUT ANY WARRANTY; without | ||
| 36 : | * even the implied warranty of MERCHANTABILITY or FITNESS | ||
| 37 : | * FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 38 : | * for more details. | ||
| 39 : | * | ||
| 40 : | * You should have received a copy of the GNU General Public | ||
| 41 : | * License along with the PHP JPEG Metadata Toolkit; if not, | ||
| 42 : | * write to the Free Software Foundation, Inc., 59 Temple | ||
| 43 : | * Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 44 : | * | ||
| 45 : | * If you require a different license for commercial or other | ||
| 46 : | * purposes, please contact the author: evan@ozhiker.com | ||
| 47 : | * | ||
| 48 : | ******************************************************************************/ | ||
| 49 : | // MOS Intruder Alerts | ||
| 50 : | defined( '_VALID_MOS' ) or die( 'Direct Access to this location is not allowed.' ); | ||
| 51 : | |||
| 52 : | |||
| 53 : | /****************************************************************************** | ||
| 54 : | * | ||
| 55 : | * Function: get_JFIF | ||
| 56 : | * | ||
| 57 : | * Description: Retrieves information from a JPEG File Interchange Format (JFIF) | ||
| 58 : | * segment and returns it in an array. Uses information supplied by | ||
| 59 : | * the get_jpeg_header_data function | ||
| 60 : | * | ||
| 61 : | * Parameters: jpeg_header_data - a JPEG header data array in the same format | ||
| 62 : | * as from get_jpeg_header_data | ||
| 63 : | * | ||
| 64 : | * Returns: JFIF_data - an array of JFIF data | ||
| 65 : | * FALSE - if a JFIF segment could not be found | ||
| 66 : | * | ||
| 67 : | ******************************************************************************/ | ||
| 68 : | |||
| 69 : | function get_JFIF( $jpeg_header_data ) | ||
| 70 : | { | ||
| 71 : | //Cycle through the header segments | ||
| 72 : | for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) | ||
| 73 : | { | ||
| 74 : | // If we find an APP0 header, | ||
| 75 : | if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) | ||
| 76 : | { | ||
| 77 : | // And if it has the JFIF label, | ||
| 78 : | if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFIF\x00", 5) == 0 ) | ||
| 79 : | { | ||
| 80 : | // Found a JPEG File Interchange Format (JFIF) Block | ||
| 81 : | |||
| 82 : | // unpack the JFIF data from the incoming string | ||
| 83 : | // First is the JFIF label string | ||
| 84 : | // Then a two byte version number | ||
| 85 : | // Then a byte, units identifier, ( 0 = aspect ration, 1 = dpi, 2 = dpcm) | ||
| 86 : | // Then a two byte int X-Axis pixel Density (resolution) | ||
| 87 : | // Then a two byte int Y-Axis pixel Density (resolution) | ||
| 88 : | // Then a byte X-Axis JFIF thumbnail size | ||
| 89 : | // Then a byte Y-Axis JFIF thumbnail size | ||
| 90 : | // Then the uncompressed RGB JFIF thumbnail data | ||
| 91 : | |||
| 92 : | $JFIF_data = unpack( 'a5JFIF/C2Version/CUnits/nXDensity/nYDensity/CThumbX/CThumbY/a*ThumbData', $jpeg_header_data[$i]['SegData'] ); | ||
| 93 : | |||
| 94 : | return $JFIF_data; | ||
| 95 : | } | ||
| 96 : | } | ||
| 97 : | } | ||
| 98 : | return FALSE; | ||
| 99 : | } | ||
| 100 : | |||
| 101 : | /****************************************************************************** | ||
| 102 : | * End of Function: get_JFIF | ||
| 103 : | ******************************************************************************/ | ||
| 104 : | |||
| 105 : | |||
| 106 : | |||
| 107 : | |||
| 108 : | /****************************************************************************** | ||
| 109 : | * | ||
| 110 : | * Function: put_JFIF | ||
| 111 : | * | ||
| 112 : | * Description: Creates a new JFIF segment from an array of JFIF data in the | ||
| 113 : | * same format as would be retrieved from get_JFIF, and inserts | ||
| 114 : | * this segment into the supplied JPEG header array | ||
| 115 : | * | ||
| 116 : | * Parameters: jpeg_header_data - a JPEG header data array in the same format | ||
| 117 : | * as from get_jpeg_header_data, into which the | ||
| 118 : | * new JFIF segment will be put | ||
| 119 : | * new_JFIF_array - a JFIF information array in the same format as | ||
| 120 : | * from get_JFIF, to create the new segment | ||
| 121 : | * | ||
| 122 : | * Returns: jpeg_header_data - the JPEG header data array with the new | ||
| 123 : | * JFIF segment added | ||
| 124 : | * | ||
| 125 : | ******************************************************************************/ | ||
| 126 : | |||
| 127 : | function put_JFIF( $jpeg_header_data, $new_JFIF_array ) | ||
| 128 : | { | ||
| 129 : | // pack the JFIF data into its proper format for a JPEG file | ||
| 130 : | $packed_data = pack( 'a5CCCnnCCa*',"JFIF\x00", $new_JFIF_array['Version1'], $new_JFIF_array['Version2'], $new_JFIF_array['Units'], $new_JFIF_array['XDensity'], $new_JFIF_array['YDensity'], $new_JFIF_array['ThumbX'], $new_JFIF_array['ThumbY'], $new_JFIF_array['ThumbData'] ); | ||
| 131 : | |||
| 132 : | //Cycle through the header segments | ||
| 133 : | for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) | ||
| 134 : | { | ||
| 135 : | // If we find an APP0 header, | ||
| 136 : | if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) | ||
| 137 : | { | ||
| 138 : | // And if it has the JFIF label, | ||
| 139 : | if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFIF\x00", 5) == 0 ) | ||
| 140 : | { | ||
| 141 : | // Found a preexisting JFIF block - Replace it with the new one and return. | ||
| 142 : | $jpeg_header_data[$i]['SegData'] = $packed_data; | ||
| 143 : | return $jpeg_header_data; | ||
| 144 : | } | ||
| 145 : | } | ||
| 146 : | } | ||
| 147 : | |||
| 148 : | // No preexisting JFIF block found, insert a new one at the start of the header data. | ||
| 149 : | array_splice($jpeg_header_data, 0 , 0, array( array( "SegType" => 0xE0, | ||
| 150 : | "SegName" => "APP0", | ||
| 151 : | "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], | ||
| 152 : | "SegData" => $packed_data ) ) ); | ||
| 153 : | return $jpeg_header_data; | ||
| 154 : | } | ||
| 155 : | |||
| 156 : | /****************************************************************************** | ||
| 157 : | * End of Function: put_JFIF | ||
| 158 : | ******************************************************************************/ | ||
| 159 : | |||
| 160 : | |||
| 161 : | |||
| 162 : | |||
| 163 : | |||
| 164 : | |||
| 165 : | |||
| 166 : | |||
| 167 : | /****************************************************************************** | ||
| 168 : | * | ||
| 169 : | * Function: Interpret_JFIF_to_HTML | ||
| 170 : | * | ||
| 171 : | * Description: Generates html showing the JFIF information contained in | ||
| 172 : | * a JFIF data array, as retrieved with get_JFIF | ||
| 173 : | * | ||
| 174 : | * Parameters: JFIF_array - a JFIF data array, as from get_JFIF | ||
| 175 : | * filename - the name of the JPEG file being processed ( used | ||
| 176 : | * by the script which displays the JFIF thumbnail) | ||
| 177 : | * | ||
| 178 : | * | ||
| 179 : | * Returns: output - the HTML string | ||
| 180 : | * | ||
| 181 : | ******************************************************************************/ | ||
| 182 : | |||
| 183 : | function Interpret_JFIF_to_HTML( $JFIF_array, $filename ) | ||
| 184 : | { | ||
| 185 : | $output = ""; | ||
| 186 : | if ( $JFIF_array !== FALSE ) | ||
| 187 : | { | ||
| 188 : | $output .= "<H2 class=\"JFIF_Main_Heading\">Contains JPEG File Interchange Format (JFIF) Information</H2>\n"; | ||
| 189 : | $output .= "\n<table class=\"JFIF_Table\" border=1>\n"; | ||
| 190 : | $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">JFIF version: </td><td class=\"JFIF_Value_Cell\">". sprintf( "%d.%02d", $JFIF_array['Version1'], $JFIF_array['Version2'] ) . "</td></tr>\n"; | ||
| 191 : | if ( $JFIF_array['Units'] == 0 ) | ||
| 192 : | { | ||
| 193 : | $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">Pixel Aspect Ratio: </td><td class=\"JFIF_Value_Cell\">" . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . "</td></tr>\n"; | ||
| 194 : | } | ||
| 195 : | elseif ( $JFIF_array['Units'] == 1 ) | ||
| 196 : | { | ||
| 197 : | $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">Resolution: </td><td class=\"JFIF_Value_Cell\">" . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . " pixels per inch</td></tr>\n"; | ||
| 198 : | } | ||
| 199 : | elseif ( $JFIF_array['Units'] == 2 ) | ||
| 200 : | { | ||
| 201 : | $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">Resolution: </td><td class=\"JFIF_Value_Cell\">" . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . " pixels per cm</td></tr>\n"; | ||
| 202 : | } | ||
| 203 : | |||
| 204 : | $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">JFIF (uncompressed) thumbnail: </td><td class=\"JFIF_Value_Cell\">"; | ||
| 205 : | if ( ( $JFIF_array['ThumbX'] != 0 ) && ( $JFIF_array['ThumbY'] != 0 ) ) | ||
| 206 : | { | ||
| 207 : | $output .= $JFIF_array['ThumbX'] ." x " . $JFIF_array['ThumbY'] . " pixels, Thumbnail Display Not Yet Implemented</td></tr>\n"; | ||
| 208 : | // TODO Implement JFIF Thumbnail display | ||
| 209 : | } | ||
| 210 : | else | ||
| 211 : | { | ||
| 212 : | $output .= "None</td></tr>\n"; | ||
| 213 : | } | ||
| 214 : | |||
| 215 : | $output .= "</table><br>\n"; | ||
| 216 : | } | ||
| 217 : | |||
| 218 : | return $output; | ||
| 219 : | |||
| 220 : | } | ||
| 221 : | |||
| 222 : | |||
| 223 : | /****************************************************************************** | ||
| 224 : | * End of Function: Interpret_JFIF_to_HTML | ||
| 225 : | ******************************************************************************/ | ||
| 226 : | |||
| 227 : | |||
| 228 : | |||
| 229 : | |||
| 230 : | |||
| 231 : | |||
| 232 : | |||
| 233 : | |||
| 234 : | |||
| 235 : | |||
| 236 : | /****************************************************************************** | ||
| 237 : | * | ||
| 238 : | * Function: get_JFXX | ||
| 239 : | * | ||
| 240 : | * Description: Retrieves information from a JPEG File Interchange Format Extension (JFXX) | ||
| 241 : | * segment and returns it in an array. Uses information supplied by | ||
| 242 : | * the get_jpeg_header_data function | ||
| 243 : | * | ||
| 244 : | * Parameters: jpeg_header_data - a JPEG header data array in the same format | ||
| 245 : | * as from get_jpeg_header_data | ||
| 246 : | * | ||
| 247 : | * Returns: JFXX_data - an array of JFXX data | ||
| 248 : | * FALSE - if a JFXX segment could not be found | ||
| 249 : | * | ||
| 250 : | ******************************************************************************/ | ||
| 251 : | |||
| 252 : | function get_JFXX( $jpeg_header_data ) | ||
| 253 : | { | ||
| 254 : | //Cycle through the header segments | ||
| 255 : | for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) | ||
| 256 : | { | ||
| 257 : | // If we find an APP0 header, | ||
| 258 : | if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) | ||
| 259 : | { | ||
| 260 : | // And if it has the JFIF label, | ||
| 261 : | if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFXX\x00", 5) == 0 ) | ||
| 262 : | { | ||
| 263 : | // Found a JPEG File Interchange Format Extension (JFXX) Block | ||
| 264 : | |||
| 265 : | // unpack the JFXX data from the incoming string | ||
| 266 : | // First is the 5 byte JFXX label string | ||
| 267 : | // Then a 1 byte Extension code, indicating Thumbnail Format | ||
| 268 : | // Then the thumbnail data | ||
| 269 : | |||
| 270 : | $JFXX_data = unpack( 'a5JFXX/CExtension_Code/a*ThumbData', $jpeg_header_data[$i]['SegData'] ); | ||
| 271 : | return $JFXX_data; | ||
| 272 : | } | ||
| 273 : | } | ||
| 274 : | } | ||
| 275 : | return FALSE; | ||
| 276 : | } | ||
| 277 : | |||
| 278 : | /****************************************************************************** | ||
| 279 : | * End of Function: get_JFXX | ||
| 280 : | ******************************************************************************/ | ||
| 281 : | |||
| 282 : | |||
| 283 : | |||
| 284 : | |||
| 285 : | /****************************************************************************** | ||
| 286 : | * | ||
| 287 : | * Function: put_JFXX | ||
| 288 : | * | ||
| 289 : | * Description: Creates a new JFXX segment from an array of JFXX data in the | ||
| 290 : | * same format as would be retrieved from get_JFXX, and inserts | ||
| 291 : | * this segment into the supplied JPEG header array | ||
| 292 : | * | ||
| 293 : | * Parameters: jpeg_header_data - a JPEG header data array in the same format | ||
| 294 : | * as from get_jpeg_header_data, into which the | ||
| 295 : | * new JFXX segment will be put | ||
| 296 : | * new_JFXX_array - a JFXX information array in the same format as | ||
| 297 : | * from get_JFXX, to create the new segment | ||
| 298 : | * | ||
| 299 : | * Returns: jpeg_header_data - the JPEG header data array with the new | ||
| 300 : | * JFXX segment added | ||
| 301 : | * | ||
| 302 : | ******************************************************************************/ | ||
| 303 : | |||
| 304 : | function put_JFXX( $jpeg_header_data, $new_JFXX_array ) | ||
| 305 : | { | ||
| 306 : | // pack the JFXX data into its proper format for a JPEG file | ||
| 307 : | $packed_data = pack( 'a5Ca*',"JFXX\x00", $new_JFXX_array['Extension_Code'], $new_JFXX_array['ThumbData'] ); | ||
| 308 : | |||
| 309 : | $JFIF_pos = -1; | ||
| 310 : | |||
| 311 : | //Cycle through the header segments | ||
| 312 : | for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) | ||
| 313 : | { | ||
| 314 : | // If we find an APP0 header, | ||
| 315 : | if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) | ||
| 316 : | { | ||
| 317 : | // And if it has the JFXX label, | ||
| 318 : | if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFXX\x00", 5) == 0 ) | ||
| 319 : | { | ||
| 320 : | // Found a preexisting JFXX block - Replace it with the new one and return. | ||
| 321 : | $jpeg_header_data[$i]['SegData'] = $packed_data; | ||
| 322 : | return $jpeg_header_data; | ||
| 323 : | } | ||
| 324 : | |||
| 325 : | // if it has the JFIF label, | ||
| 326 : | if( strncmp ( $jpeg_header_data[$i][SegData], "JFIF\x00", 5) == 0 ) | ||
| 327 : | { | ||
| 328 : | // Found a preexisting JFIF block - Mark it in case we need to insert the JFXX after it | ||
| 329 : | $JFIF_pos = $i; | ||
| 330 : | } | ||
| 331 : | } | ||
| 332 : | } | ||
| 333 : | |||
| 334 : | |||
| 335 : | // No preexisting JFXX block found | ||
| 336 : | |||
| 337 : | // Check if a JFIF segment was found, | ||
| 338 : | if ( $JFIF_pos !== -1 ) | ||
| 339 : | { | ||
| 340 : | // A pre-existing JFIF segment was found, | ||
| 341 : | // insert the new JFXX segment after it. | ||
| 342 : | array_splice($jpeg_header_data, $JFIF_pos +1 , 0, array ( array( "SegType" => 0xE0, | ||
| 343 : | "SegName" => "APP0", | ||
| 344 : | "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], | ||
| 345 : | "SegData" => $packed_data ) ) ); | ||
| 346 : | |||
| 347 : | } | ||
| 348 : | else | ||
| 349 : | { | ||
| 350 : | // No pre-existing JFIF segment was found, | ||
| 351 : | // insert a new JFIF and the new JFXX segment at the start of the array. | ||
| 352 : | |||
| 353 : | // Insert new JFXX segment | ||
| 354 : | array_splice($jpeg_header_data, 0 , 0, array( array( "SegType" => 0xE0, | ||
| 355 : | "SegName" => "APP0", | ||
| 356 : | "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], | ||
| 357 : | "SegData" => $packed_data ) ) ); | ||
| 358 : | |||
| 359 : | // Create a new JFIF to be inserted at the start of | ||
| 360 : | // the array, with generic values | ||
| 361 : | $packed_data = pack( 'a5CCCnnCCa*',"JFIF\x00", 1, 2, 1, 72, 72, 0, 0, "" ); | ||
| 362 : | |||
| 363 : | array_splice($jpeg_header_data, 0 , 0, array( array( "SegType" => 0xE0, | ||
| 364 : | "SegName" => "APP0", | ||
| 365 : | "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], | ||
| 366 : | "SegData" => $packed_data ) ) ); | ||
| 367 : | } | ||
| 368 : | |||
| 369 : | |||
| 370 : | return $jpeg_header_data; | ||
| 371 : | } | ||
| 372 : | |||
| 373 : | /****************************************************************************** | ||
| 374 : | * End of Function: put_JFIF | ||
| 375 : | ******************************************************************************/ | ||
| 376 : | |||
| 377 : | |||
| 378 : | |||
| 379 : | /****************************************************************************** | ||
| 380 : | * | ||
| 381 : | * Function: Interpret_JFXX_to_HTML | ||
| 382 : | * | ||
| 383 : | * Description: Generates html showing the JFXX thumbnail contained in | ||
| 384 : | * a JFXX data array, as retrieved with get_JFXX | ||
| 385 : | * | ||
| 386 : | * Parameters: JFXX_array - a JFXX information array in the same format as | ||
| 387 : | * from get_JFXX, to create the new segment | ||
| 388 : | * filename - the name of the JPEG file being processed ( used | ||
| 389 : | * by the script which displays the JFXX thumbnail) | ||
| 390 : | * | ||
| 391 : | * Returns: output - the Html string | ||
| 392 : | * | ||
| 393 : | ******************************************************************************/ | ||
| 394 : | |||
| 395 : | function Interpret_JFXX_to_HTML( $JFXX_array, $filename ) | ||
| 396 : | { | ||
| 397 : | $output = ""; | ||
| 398 : | if ( $JFXX_array !== FALSE ) | ||
| 399 : | { | ||
| 400 : | $output .= "<H2 class=\"JFXX_Main_Heading\">Contains JPEG File Interchange Extension Format (JFXX) Thumbnail</H2>\n"; | ||
| 401 : | switch ( $JFXX_array['Extension_Code'] ) | ||
| 402 : | { | ||
| 403 : | case 0x10 : $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is JPEG Encoded</p>\n"; | ||
| 404 : | $output .= "<a class=\"JFXX_Thumbnail_Link\" href=\"get_jfxx_thumb.php?filename=$filename\"><img class=\"JFXX_Thumbnail\" src=\"get_jfxx_thumb.php?filename=$filename\"></a>\n"; | ||
| 405 : | break; | ||
| 406 : | case 0x11 : $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is Encoded 1 byte/pixel</p>\n"; | ||
| 407 : | $output .= "<p class=\"JFXX_Text\">Thumbnail Display Not Implemented Yet</p>\n"; | ||
| 408 : | break; | ||
| 409 : | case 0x13 : $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is Encoded 3 bytes/pixel</p>\n"; | ||
| 410 : | $output .= "<p class=\"JFXX_Text\">Thumbnail Display Not Implemented Yet</p>\n"; | ||
| 411 : | break; | ||
| 412 : | default : $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is Encoded with Unknown format</p>\n"; | ||
| 413 : | break; | ||
| 414 : | |||
| 415 : | // TODO: Implement JFXX one and three bytes per pixel thumbnail decoding | ||
| 416 : | } | ||
| 417 : | |||
| 418 : | } | ||
| 419 : | |||
| 420 : | return $output; | ||
| 421 : | |||
| 422 : | } | ||
| 423 : | |||
| 424 : | /****************************************************************************** | ||
| 425 : | * End of Function: Interpret_JFXX_to_HTML | ||
| 426 : | ******************************************************************************/ | ||
| 427 : | |||
| 428 : | |||
| 429 : | |||
| 430 : | |||
| 431 : | ?> |
| ViewVC Help | |
| Powered by ViewVC 1.0.0 |
Web Hosting provided by Network Redux.

