From: maxious Date: Tue, 01 Mar 2011 12:02:50 +0000 Subject: Add query to collate routes serviced at a stop X-Git-Url: http://maxious.lambdacomplex.org/git/?p=busui.git&a=commitdiff&h=7cbb8e3d9929d320419514ed242913d9eb28b9fd --- Add query to collate routes serviced at a stop --- --- a/aws/awsStartup.sh +++ b/aws/awsStartup.sh @@ -5,6 +5,7 @@ #http://www.how2forge.org/installing-lighttpd-with-php5-and-mysql-support-on-fedora-12 cp -rfv /tmp/busui/* /var/www +cp /root/aws.php /tmp/ chcon -h system_u:object_r:httpd_sys_content_t /var/www chcon -R -h root:object_r:httpd_sys_content_t /var/www/* chcon -R -t httpd_sys_content_rw_t /var/www/staticmaplite/cache --- a/common.inc.php +++ b/common.inc.php @@ -4,7 +4,10 @@ $cloudmadeAPIkey="daa03470bb8740298d4b10e3f03d63e6"; $googleMapsAPIkey="ABQIAAAA95XYXN0cki3Yj_Sb71CFvBTPaLd08ONybQDjcH_VdYtHHLgZvRTw2INzI_m17_IoOUqH3RNNmlTk1Q"; $otpAPIurl = 'http://localhost:8080/opentripplanner-api-webapp/'; -if (isDebug()) error_reporting(E_ALL ^ E_NOTICE); +$owaSiteID = 'fe5b819fa8c424a99ff0764d955d23f3'; +//$debugOkay = Array("session","json","phperror","other"); +$debugOkay = Array("session","json","phperror"); +if (isDebug("phperror")) error_reporting(E_ALL ^ E_NOTICE); // SELECT array_to_string(array(SELECT REPLACE(name_2006, ',', '\,') as name FROM suburbs order by name), ',') $suburbs = explode(",","Acton,Ainslie,Amaroo,Aranda,Banks,Barton,Belconnen,Bonner,Bonython,Braddon,Bruce,Calwell,Campbell,Chapman,Charnwood,Chifley,Chisholm,City,Conder,Cook,Curtin,Deakin,Dickson,Downer,Duffy,Dunlop,Evatt,Fadden,Farrer,Fisher,Florey,Flynn,Forrest,Franklin,Fraser,Fyshwick,Garran,Gilmore,Giralang,Gordon,Gowrie,Greenway,Griffith,Gungahlin,Hackett,Hall,Harrison,Hawker,Higgins,Holder,Holt,Hughes,Hume,Isaacs,Isabella Plains,Kaleen,Kambah,Kingston,Latham,Lawson,Lyneham,Lyons,Macarthur,Macgregor,Macquarie,Mawson,McKellar,Melba,Mitchell,Monash,Narrabundah,Ngunnawal,Nicholls,Oaks Estate,O'Connor,O'Malley,Oxley,Page,Palmerston,Parkes,Pearce,Phillip,Pialligo,Red Hill,Reid,Richardson,Rivett,Russell,Scullin,Spence,Stirling,Symonston,Tharwa,Theodore,Torrens,Turner,Wanniassa,Waramanga,Watson,Weetangera,Weston,Yarralumla"); @@ -18,37 +21,58 @@ $_SESSION['time'] = filter_var($_REQUEST['time'],FILTER_SANITIZE_STRING); } if (isset($_REQUEST['geolocate'])) { + $geocoded = false; if (isset($_REQUEST['lat']) && isset($_REQUEST['lon'])) { $_SESSION['lat'] = $_REQUEST['lat']; $_SESSION['lon'] = $_REQUEST['lon']; } else { - $contents = geocode(var_filter($_REQUEST['geolocate'],FILTER_SANITIZE_URL),true); + $contents = geocode(filter_var($_REQUEST['geolocate'],FILTER_SANITIZE_URL),true); if (isset($contents[0]->centroid)) { + $geocoded = true; $_SESSION['lat'] = $contents[0]->centroid->coordinates[0]; $_SESSION['lon'] = $contents[0]->centroid->coordinates[1]; - } - else { + } + else { $_SESSION['lat'] = ""; $_SESSION['lon'] = ""; } } + if ($_SESSION['lat'] != "" && isMetricsOn()) { +// Create a new Instance of the tracker +$owa = new owa_php($config); +// Set the ID of the site being tracked +$owa->setSiteId($owaSiteID); +// Create a new event object +$event = $owa->makeEvent(); +// Set the Event Type, in this case a "video_play" +$event->setEventType('geolocate'); +// Set a property +$event->set('lat',$_SESSION['lat']); +$event->set('lon',$_SESSION['lon']); +$event->set('geocoded',$geocoded); +// Track the event +$owa->trackEvent($event); + } } -//print_r ($_SESSION); -function isDebug() -{ - return $_SERVER['SERVER_NAME'] == "10.0.1.154" || $_SERVER['SERVER_NAME'] == "localhost" || $_SERVER['SERVER_NAME'] == "127.0.0.1" || !$_SERVER['SERVER_NAME']; +debug(print_r($_SESSION,true)); +function isDebug($debugReason = "other") +{ + global $debugOkay; + return in_array($debugReason,$debugOkay,false) && $_SERVER['SERVER_NAME'] == "10.0.1.154" || $_SERVER['SERVER_NAME'] == "localhost" || $_SERVER['SERVER_NAME'] == "127.0.0.1" || !$_SERVER['SERVER_NAME']; } function isMetricsOn() { - return false; -} - -function debug($msg) { - if (isDebug()) echo "\n\n"; + return !isDebug(); +} + +function debug($msg, $debugReason = "other") { + if (isDebug($debugReason)) echo "\n\n"; } function isFastDevice() { + $ua = $_SERVER['HTTP_USER_AGENT']; $fastDevices = Array("Mozilla/5.0 (X11;", "Mozilla/5.0 (Windows;", "Mozilla/5.0 (iP", "Mozilla/5.0 (Linux; U; Android", "Mozilla/4.0 (compatible; MSIE"); + $slowDevices = Array("J2ME","MIDP","Opera/","Mozilla/2.0 (compatible;","Mozilla/3.0 (compatible;"); return true; } @@ -105,7 +129,7 @@ function success(position) { $('#geolocate').val(position.coords.latitude+','+position.coords.longitude); -// setCookie('geolocate',position.coords.latitude+','+position.coords.longitude,1); +$.ajax({ url: \"common.inc.php?geolocate=yes&lat=\"+position.coords.latitude+\"&lon=\"+position.coords.longitude }); $('#here').click(function(event) { $('#geolocate').val(doAJAXrequestForGeolocSessionHere()); return false;}); $('#here').show(); } @@ -126,7 +150,8 @@ require_once('owa/owa_env.php'); require_once(OWA_DIR.'owa_php.php'); $owa = new owa_php(); - $owa->setSiteId('bus.lambdacomplex.org'); + global $owaSiteID; + $owa->setSiteId($owaSiteID); $owa->setPageTitle($pageTitle); $owa->setPageType($pageType); $owa->trackPageView(); @@ -221,7 +246,15 @@ return $f; } -function staticmap($mapPoints, $zoom = 0, $markerImage = "iconb") +function curPageURL() { +$isHTTPS = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on"); +$port = (isset($_SERVER["SERVER_PORT"]) && ((!$isHTTPS && $_SERVER["SERVER_PORT"] != "80") || ($isHTTPS && $_SERVER["SERVER_PORT"] != "443"))); +$port = ($port) ? ':'.$_SERVER["SERVER_PORT"] : ''; +$url = ($isHTTPS ? 'https://' : 'http://').$_SERVER["SERVER_NAME"].$port.dirname($_SERVER['PHP_SELF'])."/"; +return $url; +} + +function staticmap($mapPoints, $zoom = 0, $markerImage = "iconb", $collapsible = true) { $width = 300; $height = 300; @@ -245,7 +278,7 @@ if (sizeof($mapPoints) === 1) { if ($zoom == 0) $zoom = 14; $markers .= "{$mapPoints[0][0]},{$mapPoints[0][1]},$markerimage"; - $center = "{$mapPoints[0][0]},{$mapPoints[0][1]}"; + $center = "{$mapPoints[0][0]},{$mapPoints[0][1]}"; } else { foreach ($mapPoints as $index => $mapPoint) { $markers .= $mapPoint[0].",".$mapPoint[1].",".$markerImage.($index+1); @@ -267,9 +300,9 @@ $center = $totalLat/sizeof($mapPoints).",".$totalLon/sizeof($mapPoints); } $output = ""; - if(basename($_SERVER['PHP_SELF']) != "tripPlanner.php") $output .= '

Open Map...

'; - $output .= '
'; - if(basename($_SERVER['PHP_SELF']) != "tripPlanner.php") $output .= '
'; + if ($collapsible) $output .= '

Open Map...

'; + $output .= '
'; + if ($collapsible) $output .= '
'; return $output; } @@ -454,11 +487,11 @@
- +
- Current Time? + Current Time?
@@ -475,5 +508,7 @@
'; } + + ?> --- a/feedback.php +++ b/feedback.php @@ -1,18 +1,54 @@ verifyEmailAddress($address); +//$con->listVerifiedEmailAddresses(); + +$m = new SimpleEmailServiceMessage(); +$m->addTo($address); +$m->setFrom($address); +$m->setSubject($topic); +$m->setMessageFromString($message); +$con->sendEmail($m); +} else { +// In case any of our lines are larger than 70 characters, we should use wordwrap() +$message = wordwrap($message, 70); + +// Send +mail($address, $topic, $message); +} +} + + ?>

Add/Move/Delete a Bus Stop Location

StopID: or StopCode: + if you click on feedback from a stop page, these will get filled in automatically. else describe the location/street of the stop Suggested Stop Location (lat/long or words): + if your device supports javascript, you can pick a location from the map above + +Submit!

Bug Report/Feedback

+Please leave feedback about bugs/errors or general suggestions about improvements that could be made to the way the data is presented! + + +Submit! --- a/layar_api.php +++ b/layar_api.php @@ -26,7 +26,7 @@ $hotspot['distance'] = distance($row[2], $row[3], $_REQUEST['lat'], $_REQUEST['lon']); if (!isset($_REQUEST['radius']) || $hotspot['distance'] < $_REQUEST['radius']) { $hotspot['actions'] = Array(Array("label" => 'View more trips/information', 'uri' => 'http://bus.lambdacomplex.org/'.'stop.php?stopid='.$row[0])); - $url = $APIurl."/json/stoptrips?stop=".$row[0]."&time=".midnight_seconds()."&service_period=".service_period(); + $url = $APIurl."/json/stoptrips?stop=".$row[0]."&time=".midnight_seconds()."&service_period=".service_period()."&limit=4&time_range=".str(90*60); $trips = json_decode(getPage($url)); debug(print_r($trips,true)); foreach ($trips as $key => $row) --- a/schedule_viewer.py +++ b/schedule_viewer.py @@ -421,22 +421,71 @@ if s.stop_id.lower() == query: return StopToTuple(s) return [] + def handle_json_GET_stoproutes(self, params): + """Given a stop_id return all routes to visit the stop.""" + schedule = self.server.schedule + stop = schedule.GetStop(params.get('stop', None)) + service_period = params.get('service_period', None) + trips = stop.GetTrips(schedule) + result = {} + for trip in trips: + route = schedule.GetRoute(trip.route_id) + if not trip.route_id in result: + result[trip.route_id] = (route.route_id, route.route_short_name, route.route_long_name, trip.trip_id) + return result + + def handle_json_GET_stopalltrips(self, params): + """Given a stop_id return all trips to visit the stop.""" + schedule = self.server.schedule + stop = schedule.GetStop(params.get('stop', None)) + service_period = params.get('service_period', None) + time_trips = stop.GetStopTimeTrips(schedule) + result = [] + for time, (trip, index), tp in time_trips: + headsign = None + # Find the most recent headsign from the StopTime objects + for stoptime in trip.GetStopTimes()[index::-1]: + if stoptime.stop_headsign: + headsign = stoptime.stop_headsign + break + # If stop_headsign isn't found, look for a trip_headsign + if not headsign: + headsign = trip.trip_headsign + route = schedule.GetRoute(trip.route_id) + trip_name = '' + if route.route_short_name: + trip_name += route.route_short_name + if route.route_long_name: + if len(trip_name): + trip_name += " - " + trip_name += route.route_long_name + if service_period == None or trip.service_id == service_period: + result.append((time, (trip.trip_id, trip_name, trip.service_id), tp)) + return result def handle_json_GET_stoptrips(self, params): """Given a stop_id and time in seconds since midnight return the next trips to visit the stop.""" schedule = self.server.schedule stop = schedule.GetStop(params.get('stop', None)) - time = int(params.get('time', 0)) + requested_time = int(params.get('time', 0)) + limit = int(params.get('limit', 15)) service_period = params.get('service_period', None) - time_trips = stop.GetStopTimeTrips(schedule) - time_trips.sort() # OPT: use bisect.insort to make this O(N*ln(N)) -> O(N) - # Keep the first 15 after param 'time'. - # Need make a tuple to find correct bisect point - time_trips = time_trips[bisect.bisect_left(time_trips, (time, 0)):] - time_trips = time_trips[:15] + time_range = params.get('time_range', 24*60*60) + + + filtered_time_trips = [] + for trip, index in stop._GetTripIndex(schedule): + tripstarttime = trip.GetStartTime() + if tripstarttime > requested_time and tripstarttime < (requested_time + time_range): + time, stoptime, tp = trip.GetTimeInterpolatedStops()[index] + if time > requested_time and time < (requested_time + time_range): + bisect.insort(filtered_time_trips, (time, (trip, index), tp)) + result = [] - for time, (trip, index), tp in time_trips: + for time, (trip, index), tp in filtered_time_trips: + if len(result) > limit: + break headsign = None # Find the most recent headsign from the StopTime objects for stoptime in trip.GetStopTimes()[index::-1]: --- /dev/null +++ b/ses.php @@ -1,1 +1,704 @@ - +__accessKey; } + public function getSecretKey() { return $this->__secretKey; } + public function getHost() { return $this->__host; } + + protected $__verifyHost = 1; + protected $__verifyPeer = 1; + + // verifyHost and verifyPeer determine whether curl verifies ssl certificates. + // It may be necessary to disable these checks on certain systems. + // These only have an effect if SSL is enabled. + public function verifyHost() { return $this->__verifyHost; } + public function enableVerifyHost($enable = true) { $this->__verifyHost = $enable; } + + public function verifyPeer() { return $this->__verifyPeer; } + public function enableVerifyPeer($enable = true) { $this->__verifyPeer = $enable; } + + /** + * Constructor + * + * @param string $accessKey Access key + * @param string $secretKey Secret key + * @return void + */ + public function __construct($accessKey = null, $secretKey = null, $host = 'email.us-east-1.amazonaws.com') { + if ($accessKey !== null && $secretKey !== null) { + $this->setAuth($accessKey, $secretKey); + } + $this->__host = $host; + } + + /** + * Set AWS access key and secret key + * + * @param string $accessKey Access key + * @param string $secretKey Secret key + * @return void + */ + public function setAuth($accessKey, $secretKey) { + $this->__accessKey = $accessKey; + $this->__secretKey = $secretKey; + } + + /** + * Lists the email addresses that have been verified and can be used as the 'From' address + * + * @return An array containing two items: a list of verified email addresses, and the request id. + */ + public function listVerifiedEmailAddresses() { + $rest = new SimpleEmailServiceRequest($this, 'GET'); + $rest->setParameter('Action', 'ListVerifiedEmailAddresses'); + + $rest = $rest->getResponse(); + if($rest->error === false && $rest->code !== 200) { + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + } + if($rest->error !== false) { + $this->__triggerError('listVerifiedEmailAddresses', $rest->error); + return false; + } + + $response = array(); + if(!isset($rest->body)) { + return $response; + } + + $addresses = array(); + foreach($rest->body->ListVerifiedEmailAddressesResult->VerifiedEmailAddresses->member as $address) { + $addresses[] = (string)$address; + } + + $response['Addresses'] = $addresses; + $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; + + return $response; + } + + /** + * Requests verification of the provided email address, so it can be used + * as the 'From' address when sending emails through SimpleEmailService. + * + * After submitting this request, you should receive a verification email + * from Amazon at the specified address containing instructions to follow. + * + * @param string email The email address to get verified + * @return The request id for this request. + */ + public function verifyEmailAddress($email) { + $rest = new SimpleEmailServiceRequest($this, 'POST'); + $rest->setParameter('Action', 'VerifyEmailAddress'); + $rest->setParameter('EmailAddress', $email); + + $rest = $rest->getResponse(); + if($rest->error === false && $rest->code !== 200) { + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + } + if($rest->error !== false) { + $this->__triggerError('verifyEmailAddress', $rest->error); + return false; + } + + $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; + return $response; + } + + /** + * Removes the specified email address from the list of verified addresses. + * + * @param string email The email address to remove + * @return The request id for this request. + */ + public function deleteVerifiedEmailAddress($email) { + $rest = new SimpleEmailServiceRequest($this, 'DELETE'); + $rest->setParameter('Action', 'DeleteVerifiedEmailAddress'); + $rest->setParameter('EmailAddress', $email); + + $rest = $rest->getResponse(); + if($rest->error === false && $rest->code !== 200) { + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + } + if($rest->error !== false) { + $this->__triggerError('deleteVerifiedEmailAddress', $rest->error); + return false; + } + + $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; + return $response; + } + + /** + * Retrieves information on the current activity limits for this account. + * See http://docs.amazonwebservices.com/ses/latest/APIReference/API_GetSendQuota.html + * + * @return An array containing information on this account's activity limits. + */ + public function getSendQuota() { + $rest = new SimpleEmailServiceRequest($this, 'GET'); + $rest->setParameter('Action', 'GetSendQuota'); + + $rest = $rest->getResponse(); + if($rest->error === false && $rest->code !== 200) { + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + } + if($rest->error !== false) { + $this->__triggerError('getSendQuota', $rest->error); + return false; + } + + $response = array(); + if(!isset($rest->body)) { + return $response; + } + + $response['Max24HourSend'] = (string)$rest->body->GetSendQuotaResult->Max24HourSend; + $response['MaxSendRate'] = (string)$rest->body->GetSendQuotaResult->MaxSendRate; + $response['SentLast24Hours'] = (string)$rest->body->GetSendQuotaResult->SentLast24Hours; + $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; + + return $response; + } + + /** + * Retrieves statistics for the last two weeks of activity on this account. + * See http://docs.amazonwebservices.com/ses/latest/APIReference/API_GetSendStatistics.html + * + * @return An array of activity statistics. Each array item covers a 15-minute period. + */ + public function getSendStatistics() { + $rest = new SimpleEmailServiceRequest($this, 'GET'); + $rest->setParameter('Action', 'GetSendStatistics'); + + $rest = $rest->getResponse(); + if($rest->error === false && $rest->code !== 200) { + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + } + if($rest->error !== false) { + $this->__triggerError('getSendStatistics', $rest->error); + return false; + } + + $response = array(); + if(!isset($rest->body)) { + return $response; + } + + $datapoints = array(); + foreach($rest->body->GetSendStatisticsResult->SendDataPoints->member as $datapoint) { + $p = array(); + $p['Bounces'] = (string)$datapoint->Bounces; + $p['Complaints'] = (string)$datapoint->Complaints; + $p['DeliveryAttempts'] = (string)$datapoint->DeliveryAttempts; + $p['Rejects'] = (string)$datapoint->Rejects; + $p['Timestamp'] = (string)$datapoint->Timestamp; + + $datapoints[] = $p; + } + + $response['SendDataPoints'] = $datapoints; + $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; + + return $response; + } + + + /** + * Given a SimpleEmailServiceMessage object, submits the message to the service for sending. + * + * @return An array containing the unique identifier for this message and a separate request id. + * Returns false if the provided message is missing any required fields. + */ + public function sendEmail($sesMessage) { + if(!$sesMessage->validate()) { + return false; + } + + $rest = new SimpleEmailServiceRequest($this, 'POST'); + $rest->setParameter('Action', 'SendEmail'); + + $i = 1; + foreach($sesMessage->to as $to) { + $rest->setParameter('Destination.ToAddresses.member.'.$i, $to); + $i++; + } + + if(is_array($sesMessage->cc)) { + $i = 1; + foreach($sesMessage->cc as $cc) { + $rest->setParameter('Destination.CcAddresses.member.'.$i, $cc); + $i++; + } + } + + if(is_array($sesMessage->bcc)) { + $i = 1; + foreach($sesMessage->bcc as $bcc) { + $rest->setParameter('Destination.BccAddresses.member.'.$i, $bcc); + $i++; + } + } + + if(is_array($sesMessage->replyto)) { + $i = 1; + foreach($sesMessage->replyto as $replyto) { + $rest->setParameter('ReplyToAddresses.member.'.$i, $replyto); + $i++; + } + } + + $rest->setParameter('Source', $sesMessage->from); + + if($sesMessage->returnpath != null) { + $rest->setParameter('ReturnPath', $sesMessage->returnpath); + } + + if($sesMessage->subject != null && strlen($sesMessage->subject) > 0) { + $rest->setParameter('Message.Subject.Data', $sesMessage->subject); + if($sesMessage->subjectCharset != null && strlen($sesMessage->subjectCharset) > 0) { + $rest->setParameter('Message.Subject.Charset', $sesMessage->subjectCharset); + } + } + + + if($sesMessage->messagetext != null && strlen($sesMessage->messagetext) > 0) { + $rest->setParameter('Message.Body.Text.Data', $sesMessage->messagetext); + if($sesMessage->messageTextCharset != null && strlen($sesMessage->messageTextCharset) > 0) { + $rest->setParameter('Message.Body.Text.Charset', $sesMessage->messageTextCharset); + } + } + + if($sesMessage->messagehtml != null && strlen($sesMessage->messagehtml) > 0) { + $rest->setParameter('Message.Body.Html.Data', $sesMessage->messagehtml); + if($sesMessage->messageHtmlCharset != null && strlen($sesMessage->messageHtmlCharset) > 0) { + $rest->setParameter('Message.Body.Html.Charset', $sesMessage->messageHtmlCharset); + } + } + + $rest = $rest->getResponse(); + if($rest->error === false && $rest->code !== 200) { + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + } + if($rest->error !== false) { + $this->__triggerError('sendEmail', $rest->error); + return false; + } + + $response['MessageId'] = (string)$rest->body->SendEmailResult->MessageId; + $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; + return $response; + } + + /** + * Trigger an error message + * + * @internal Used by member functions to output errors + * @param array $error Array containing error information + * @return string + */ + public function __triggerError($functionname, $error) + { + if($error == false) { + trigger_error(sprintf("SimpleEmailService::%s(): Encountered an error, but no description given", $functionname), E_USER_WARNING); + } + else if(isset($error['curl']) && $error['curl']) + { + trigger_error(sprintf("SimpleEmailService::%s(): %s %s", $functionname, $error['code'], $error['message']), E_USER_WARNING); + } + else if(isset($error['Error'])) + { + $e = $error['Error']; + $message = sprintf("SimpleEmailService::%s(): %s - %s: %s\nRequest Id: %s\n", $functionname, $e['Type'], $e['Code'], $e['Message'], $error['RequestId']); + trigger_error($message, E_USER_WARNING); + } + } + + /** + * Callback handler for 503 retries. + * + * @internal Used by SimpleDBRequest to call the user-specified callback, if set + * @param $attempt The number of failed attempts so far + * @return The retry delay in microseconds, or 0 to stop retrying. + */ + public function __executeServiceTemporarilyUnavailableRetryDelay($attempt) + { + if(is_callable($this->__serviceUnavailableRetryDelayCallback)) { + $callback = $this->__serviceUnavailableRetryDelayCallback; + return $callback($attempt); + } + return 0; + } +} + +final class SimpleEmailServiceRequest +{ + private $ses, $verb, $parameters = array(); + public $response; + + /** + * Constructor + * + * @param string $ses The SimpleEmailService object making this request + * @param string $action action + * @param string $verb HTTP verb + * @return mixed + */ + function __construct($ses, $verb) { + $this->ses = $ses; + $this->verb = $verb; + $this->response = new STDClass; + $this->response->error = false; + } + + /** + * Set request parameter + * + * @param string $key Key + * @param string $value Value + * @param boolean $replace Whether to replace the key if it already exists (default true) + * @return void + */ + public function setParameter($key, $value, $replace = true) { + if(!$replace && isset($this->parameters[$key])) + { + $temp = (array)($this->parameters[$key]); + $temp[] = $value; + $this->parameters[$key] = $temp; + } + else + { + $this->parameters[$key] = $value; + } + } + + /** + * Get the response + * + * @return object | false + */ + public function getResponse() { + + $params = array(); + foreach ($this->parameters as $var => $value) + { + if(is_array($value)) + { + foreach($value as $v) + { + $params[] = $var.'='.$this->__customUrlEncode($v); + } + } + else + { + $params[] = $var.'='.$this->__customUrlEncode($value); + } + } + + sort($params, SORT_STRING); + + // must be in format 'Sun, 06 Nov 1994 08:49:37 GMT' + $date = gmdate('D, d M Y H:i:s e'); + + $query = implode('&', $params); + + $headers = array(); + $headers[] = 'Date: '.$date; + $headers[] = 'Host: '.$this->ses->getHost(); + + $auth = 'AWS3-HTTPS AWSAccessKeyId='.$this->ses->getAccessKey(); + $auth .= ',Algorithm=HmacSHA256,Signature='.$this->__getSignature($date); + $headers[] = 'X-Amzn-Authorization: '.$auth; + + $url = 'https://'.$this->ses->getHost().'/'; + + // Basic setup + $curl = curl_init(); + curl_setopt($curl, CURLOPT_USERAGENT, 'SimpleEmailService/php'); + + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, ($this->ses->verifyHost() ? 1 : 0)); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, ($this->ses->verifyPeer() ? 1 : 0)); + + // Request types + switch ($this->verb) { + case 'GET': + $url .= '?'.$query; + break; + case 'POST': + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); + curl_setopt($curl, CURLOPT_POSTFIELDS, $query); + $headers[] = 'Content-Type: application/x-www-form-urlencoded'; + break; + case 'DELETE': + $url .= '?'.$query; + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); + break; + default: break; + } + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + curl_setopt($curl, CURLOPT_HEADER, false); + + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, false); + curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback')); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + + // Execute, grab errors + if (curl_exec($curl)) { + $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE); + } else { + $this->response->error = array( + 'curl' => true, + 'code' => curl_errno($curl), + 'message' => curl_error($curl), + 'resource' => $this->resource + ); + } + + @curl_close($curl); + + // Parse body into XML + if ($this->response->error === false && isset($this->response->body)) { + $this->response->body = simplexml_load_string($this->response->body); + + // Grab SES errors + if (!in_array($this->response->code, array(200, 201, 202, 204)) + && isset($this->response->body->Error)) { + $error = $this->response->body->Error; + $output = array(); + $output['curl'] = false; + $output['Error'] = array(); + $output['Error']['Type'] = (string)$error->Type; + $output['Error']['Code'] = (string)$error->Code; + $output['Error']['Message'] = (string)$error->Message; + $output['RequestId'] = (string)$this->response->body->RequestId; + + $this->response->error = $output; + unset($this->response->body); + } + } + + return $this->response; + } + + /** + * CURL write callback + * + * @param resource &$curl CURL resource + * @param string &$data Data + * @return integer + */ + private function __responseWriteCallback(&$curl, &$data) { + $this->response->body .= $data; + return strlen($data); + } + + /** + * Contributed by afx114 + * URL encode the parameters as per http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html?Query_QueryAuth.html + * PHP's rawurlencode() follows RFC 1738, not RFC 3986 as required by Amazon. The only difference is the tilde (~), so convert it back after rawurlencode + * See: http://www.morganney.com/blog/API/AWS-Product-Advertising-API-Requires-a-Signed-Request.php + * + * @param string $var String to encode + * @return string + */ + private function __customUrlEncode($var) { + return str_replace('%7E', '~', rawurlencode($var)); + } + + /** + * Generate the auth string using Hmac-SHA256 + * + * @internal Used by SimpleDBRequest::getResponse() + * @param string $string String to sign + * @return string + */ + private function __getSignature($string) { + return base64_encode(hash_hmac('sha256', $string, $this->ses->getSecretKey(), true)); + } +} + + +final class SimpleEmailServiceMessage { + + // these are public for convenience only + // these are not to be used outside of the SimpleEmailService class! + public $to, $cc, $bcc, $replyto; + public $from, $returnpath; + public $subject, $messagetext, $messagehtml; + public $subjectCharset, $messageTextCharset, $messageHtmlCharset; + + function __construct() { + $to = array(); + $cc = array(); + $bcc = array(); + $replyto = array(); + + $from = null; + $returnpath = null; + + $subject = null; + $messagetext = null; + $messagehtml = null; + + $subjectCharset = null; + $messageTextCharset = null; + $messageHtmlCharset = null; + } + + + /** + * addTo, addCC, addBCC, and addReplyTo have the following behavior: + * If a single address is passed, it is appended to the current list of addresses. + * If an array of addresses is passed, that array is merged into the current list. + */ + function addTo($to) { + if(!is_array($to)) { + $this->to[] = $to; + } + else { + $this->to = array_merge($this->to, $to); + } + } + + function addCC($cc) { + if(!is_array($cc)) { + $this->cc[] = $cc; + } + else { + $this->cc = array_merge($this->cc, $cc); + } + } + + function addBCC($bcc) { + if(!is_array($bcc)) { + $this->bcc[] = $bcc; + } + else { + $this->bcc = array_merge($this->bcc, $bcc); + } + } + + function addReplyTo($replyto) { + if(!is_array($replyto)) { + $this->replyto[] = $replyto; + } + else { + $this->replyto = array_merge($this->replyto, $replyto); + } + } + + function setFrom($from) { + $this->from = $from; + } + + function setReturnPath($returnpath) { + $this->returnpath = $returnpath; + } + + function setSubject($subject) { + $this->subject = $subject; + } + + function setSubjectCharset($charset) { + $this->subjectCharset = $charset; + } + + function setMessageFromString($text, $html = null) { + $this->messagetext = $text; + $this->messagehtml = $html; + } + + function setMessageFromFile($textfile, $htmlfile = null) { + if(file_exists($textfile) && is_file($textfile) && is_readable($textfile)) { + $this->messagetext = file_get_contents($textfile); + } + if(file_exists($htmlfile) && is_file($htmlfile) && is_readable($htmlfile)) { + $this->messagehtml = file_get_contents($htmlfile); + } + } + + function setMessageFromURL($texturl, $htmlurl = null) { + $this->messagetext = file_get_contents($texturl); + if($htmlurl !== null) { + $this->messagehtml = file_get_contents($htmlurl); + } + } + + function setMessageCharset($textCharset, $htmlCharset = null) { + $this->messageTextCharset = $textCharset; + $this->messageHtmlCharset = $htmlCharset; + } + + /** + * Validates whether the message object has sufficient information to submit a request to SES. + * This does not guarantee the message will arrive, nor that the request will succeed; + * instead, it makes sure that no required fields are missing. + * + * This is used internally before attempting a SendEmail or SendRawEmail request, + * but it can be used outside of this file if verification is desired. + * May be useful if e.g. the data is being populated from a form; developers can generally + * use this function to verify completeness instead of writing custom logic. + * + * @return boolean + */ + public function validate() { + if(count($this->to) == 0) + return false; + if($this->from == null || strlen($this->from) == 0) + return false; + if($this->messagetext == null) + return false; + return true; + } +} + --- /dev/null +++ b/stop.pdf.php @@ -1,1 +1,185 @@ +

'.staticmap(Array(0 => Array($stop[2],$stop[3])), 0,"iconb", false).'

'; +$html .= ' '; +require_once('tcpdf/config/lang/eng.php'); +require_once('tcpdf/tcpdf.php'); + +// create new PDF document +class Custom_TCPDF extends TCPDF { + var $QRCodeURL; + + function set_QRCodeURL ($url) { + $this->QRCodeURL = $url; + } + +/** + * This method is used to render the page header. + * It is automatically called by AddPage() and could be overwritten in your own inherited class. + * @public + */ + public function Header() { + if ($this->header_xobjid < 0) { + // start a new XObject Template + $this->header_xobjid = $this->startTemplate($this->w, $this->tMargin+10); + $headerfont = $this->getHeaderFont(); + $headerdata = $this->getHeaderData(); + $this->y = $this->header_margin; + if ($this->rtl) { + $this->x = $this->w - $this->original_rMargin; + } else { + $this->x = $this->original_lMargin-10; + } + if (isset($this->QRCodeURL)) { +// QRCODE,H : QR-CODE Best error correction +$style = array( + 'border' => 1, + 'padding' => 0, + 'fgcolor' => array(0,0,0), + 'bgcolor' => false, //array(255,255,255) + 'module_width' => 1, // width of a single module in points + 'module_height' => 1 // height of a single module in points +); +$this->write2DBarcode($this->QRCodeURL, 'QRCODE,H', '', '', 25, 25, $style, 'T'); +$imgy = 50+20; + } elseif (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) { + $imgtype = $this->getImageFileType(K_PATH_IMAGES.$headerdata['logo']); + if (($imgtype == 'eps') OR ($imgtype == 'ai')) { + $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); + } elseif ($imgtype == 'svg') { + $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); + } else { + $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); + } + $imgy = $this->getImageRBY(); + } else { + $imgy = $this->y; + } + $cell_height = round(($this->cell_height_ratio * $headerfont[2]) / $this->k, 2); + // set starting margin for text data cell + if ($this->getRTL()) { + $header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1); + } else { + $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1); + } + $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1); + $this->SetTextColor(0, 0, 0); + // header title + $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1); + $this->SetX($header_x); + $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0); + // header string + $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]); + $this->SetX($header_x); + $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false); + // print an ending header line + //$this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))); + //$this->SetY((2.835 / $this->k) + max($imgy, $this->y)); + if ($this->rtl) { + $this->SetX($this->original_rMargin); + } else { + $this->SetX($this->original_lMargin); + } + //$this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C'); + $this->endTemplate(); + } + // print header template + $x = 0; + $dx = 0; + if ($this->booklet AND (($this->page % 2) == 0)) { + // adjust margins for booklet mode + $dx = ($this->original_lMargin - $this->original_rMargin); + } + if ($this->rtl) { + $x = $this->w + $dx; + } else { + $x = 0 + $dx; + } + $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false); + } + + +} +$pdf = new Custom_TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); + +// set document information +$pdf->SetCreator(PDF_CREATOR); +$pdf->SetAuthor('bus.lambdacomplex.org'); +$pdf->SetTitle($stop[1]); + +// set default header data +$pdf->SetHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, $stop[1] . " Timetable", "Some description of customization like Weekdays, 9am-10am"); +$pdf->set_QRCodeURL(curPageURL()."stop.php?stopid=".$_REQUEST['stopid']); + +// set header and footer fonts +$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN)); +$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA)); + +// set default monospaced font +$pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED); + +//set margins +$pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT); +$pdf->SetHeaderMargin(PDF_MARGIN_HEADER); +$pdf->SetFooterMargin(PDF_MARGIN_FOOTER); + +//set auto page breaks +$pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM); + +//set image scale factor +$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); + +//set some language-dependent strings +$pdf->setLanguageArray($l); + +// --------------------------------------------------------- + +// set default font subsetting mode +$pdf->setFontSubsetting(true); + +// Set font +// dejavusans is a UTF-8 Unicode font, if you only need to +// print standard ASCII chars, you can use core fonts like +// helvetica or times to reduce file size. +$pdf->SetFont('helvetica', '', 14, '', true); + +// Add a page +// This method has several options, check the source code documentation for more information. +$pdf->AddPage(); + + +// Print text using writeHTMLCell() +$pdf->writeHTMLCell($w=0, $h=0, $x='', $y='', $html, $border=0, $ln=1, $fill=0, $reseth=true, $align='', $autopadding=true); + + + +// --------------------------------------------------------- + +// Close and output PDF document +// This method has several options, check the source code documentation for more information. +$pdf->Output('example_001.pdf', 'I'); + +//============================================================+ +// END OF FILE +//============================================================+ +?> + --- a/stop.php +++ b/stop.php @@ -6,9 +6,9 @@ include_header($stop[1],"stop"); if (isMetricsOn()) { // Create a new Instance of the tracker -$owa = new owa_php($config); +$owa = new owa_php(); // Set the ID of the site being tracked -$owa->setSiteId('bus.lambdacomplex.org'); +$owa->setSiteId($owaSiteID); // Create a new event object $event = $owa->makeEvent(); // Set the Event Type, in this case a "video_play" --- a/stopList.php +++ b/stopList.php @@ -12,7 +12,6 @@ '; - timePlaceSettings(); } // By suburb if (isset($_REQUEST['suburbs'])) { @@ -30,11 +29,12 @@ $url = $APIurl."/json/stops"; include_header("All Stops","stopList"); navbar(); + timePlaceSettings(); } else if ($_REQUEST['nearby']) { $url = $APIurl."/json/neareststops?lat={$_SESSION['lat']}&lon={$_SESSION['lon']}&limit=15"; include_header("Nearby Stops","stopList"); navbar(); - timePlaceSettings(); + timePlaceSettings(true); } else if ($_REQUEST['suburb']) { $url = $APIurl."/json/stopzonesearch?q=".filter_var($_REQUEST['suburb'], FILTER_SANITIZE_STRING); include_header("Stops in ".ucwords(filter_var($_REQUEST['suburb'], FILTER_SANITIZE_STRING)),"stopList"); @@ -42,7 +42,7 @@ // Create a new Instance of the tracker $owa = new owa_php($config); // Set the ID of the site being tracked -$owa->setSiteId('bus.lambdacomplex.org'); +$owa->setSiteId($owaSiteID); // Create a new event object $event = $owa->makeEvent(); // Set the Event Type, in this case a "video_play" @@ -57,6 +57,7 @@ $url = $APIurl."/json/timingpoints"; include_header("Timing Points / Major Stops","stopList"); navbar(); + timePlaceSettings(); } echo '
Go to letter: '; foreach(range('A','Z') as $letter) @@ -84,7 +85,12 @@ echo ""; $firstletter = substr($row[1],0,1); } - echo '
  • '.bracketsMeanNewLine($row[1]).'
  • '; + echo '
  • '; + if (isset($_SESSION['lat']) && isset($_SESSION['lon'])){ + echo ''.floor(distance($row[2], $row[3], $_SESSION['lat'], $_SESSION['lon'])).'m away'; + } + echo bracketsMeanNewLine($row[1]); + echo '
  • '; } echo ''; } --- /dev/null +++ b/tcpdf/2dbarcodes.php @@ -1,1 +1,173 @@ +. +// +// See LICENSE.TXT file for more information. +// ------------------------------------------------------------------- +// +// Description : PHP class to creates array representations for +// 2D barcodes to be used with TCPDF. +// +//============================================================+ +/** + * @file + * PHP class to creates array representations for 2D barcodes to be used with TCPDF. + * @package com.tecnick.tcpdf + * @author Nicola Asuni + * @version 1.0.007 + */ + +/** + * @class TCPDF2DBarcode + * PHP class to creates array representations for 2D barcodes to be used with TCPDF (http://www.tcpdf.org). + * @package com.tecnick.tcpdf + * @version 1.0.007 + * @author Nicola Asuni + */ +class TCPDF2DBarcode { + + /** + * Array representation of barcode. + * @protected + */ + protected $barcode_array = false; + + /** + * This is the class constructor. + * Return an array representations for 2D barcodes: + * @param $code (string) code to print + * @param $type (string) type of barcode: + */ + public function __construct($code, $type) { + $this->setBarcode($code, $type); + } + + /** + * Return an array representations of barcode. + * @return array + */ + public function getBarcodeArray() { + return $this->barcode_array; + } + + /** + * Set the barcode. + * @param $code (string) code to print + * @param $type (string) type of barcode: + * @return array + */ + public function setBarcode($code, $type) { + $mode = explode(',', $type); + $qrtype = strtoupper($mode[0]); + switch ($qrtype) { + case 'QRCODE': { // QR-CODE + require_once(dirname(__FILE__).'/qrcode.php'); + if (!isset($mode[1]) OR (!in_array($mode[1],array('L','M','Q','H')))) { + $mode[1] = 'L'; // Ddefault: Low error correction + } + $qrcode = new QRcode($code, strtoupper($mode[1])); + $this->barcode_array = $qrcode->getBarcodeArray(); + break; + } + case 'PDF417': { // PDF417 (ISO/IEC 15438:2006) + require_once(dirname(__FILE__).'/pdf417.php'); + if (!isset($mode[1]) OR ($mode[1] === '')) { + $aspectratio = 2; // default aspect ratio (width / height) + } else { + $aspectratio = floatval($mode[1]); + } + if (!isset($mode[2]) OR ($mode[2] === '')) { + $ecl = -1; // default error correction level (auto) + } else { + $ecl = intval($mode[2]); + } + // set macro block + $macro = array(); + if (isset($mode[3]) AND ($mode[3] !== '') AND isset($mode[4]) AND ($mode[4] !== '') AND isset($mode[5]) AND ($mode[5] !== '')) { + $macro['segment_total'] = intval($mode[3]); + $macro['segment_index'] = intval($mode[4]); + $macro['file_id'] = strtr($mode[5], "\xff", ','); + for ($i = 0; $i < 7; ++$i) { + $o = $i + 6; + if (isset($mode[$o]) AND ($mode[$o] !== '')) { + // add option + $macro['option_'.$i] = strtr($mode[$o], "\xff", ','); + } + } + } + $qrcode = new PDF417($code, $ecl, $aspectratio, $macro); + $this->barcode_array = $qrcode->getBarcodeArray(); + break; + } + case 'RAW': + case 'RAW2': { // RAW MODE + // remove spaces + $code = preg_replace('/[\s]*/si', '', $code); + if (strlen($code) < 3) { + break; + } + if ($qrtype == 'RAW') { + // comma-separated rows + $rows = explode(',', $code); + } else { // RAW2 + // rows enclosed in square parentheses + $code = substr($code, 1, -1); + $rows = explode('][', $code); + } + $this->barcode_array['num_rows'] = count($rows); + $this->barcode_array['num_cols'] = strlen($rows[0]); + $this->barcode_array['bcode'] = array(); + foreach ($rows as $r) { + $this->barcode_array['bcode'][] = str_split($r, 1); + } + break; + } + case 'TEST': { // TEST MODE + $this->barcode_array['num_rows'] = 5; + $this->barcode_array['num_cols'] = 15; + $this->barcode_array['bcode'] = array( + array(1,1,1,0,1,1,1,0,1,1,1,0,1,1,1), + array(0,1,0,0,1,0,0,0,1,0,0,0,0,1,0), + array(0,1,0,0,1,1,0,0,1,1,1,0,0,1,0), + array(0,1,0,0,1,0,0,0,0,0,1,0,0,1,0), + array(0,1,0,0,1,1,1,0,1,1,1,0,0,1,0)); + break; + } + default: { + $this->barcode_array = false; + } + } + } +} // end of class + +//============================================================+ +// END OF FILE +//============================================================+ + --- /dev/null +++ b/tcpdf/CHANGELOG.TXT @@ -1,1 +1,1957 @@ - +5.9.059 (2011-02-27) + - Default Header() method was improved to reduce document size. + +5.9.058 (2011-02-25) + - Image() method was improved to cache images with transparency layers (thanks to Korneliusz Jarzębski for reporting this problem). + +5.9.057 (2011-02-24) + - A problem with image caching system was fixed (thanks to Korneliusz Jarzębski for reporting this problem). + +5.9.056 (2011-02-22) + - A bug on fixHTMLCode() method was fixed. + - Automatic line break for HTML was fixed. + +5.9.055 (2011-02-17) + - Another bug related to HTML table page break was fixed. + +5.9.054 (2011-02-16) + - A bug related to HTML table page break was fixed. + +5.9.053 (2011-02-16) + - Support for HTMl attribute display="none" was added. + +5.9.052 (2011-02-15) + - A bug related to HTML automatic newlines was fixed. + +5.9.051 (2011-02-12) + - "Commas at beginning of new lines" problem was fixed. + +5.9.050 (2011-02-11) + - Bug #3177606 "SVG Bar chart error" was fixed. + +5.9.049 (2011-02-03) + - Bug #3170777 "TCPDF creates a new page after a single line in writeHTML" was fixed. + +5.9.048 (2011-02-02) + - No changes. Just released to override previous release that was not uploaded correctly. + +5.9.047 (2011-01-28) + - Bug #3167115 "PDF error in (example 48)" was fixed (was introduced in 5.8.046). + +5.9.046 (2011-01-18) + - PDF view/print layers are now automatically turned off if not used (see setVisibility() method). + +5.9.045 (2011-01-17) + - HTML list support were improved. + +5.9.044 (2011-01-15) + - Bug #3158422 "writeHTMLCell Loop" was fixed. + - Some HTML image alignment problems were fixed. + +5.9.043 (2011-01-14) + - Bug #3158178 "PHP Notice" was fixed. + - Bug #3158193 "Endless loop in writeHTML" was fixed. + - Bug #3157764 "SVG Pie chart incorrectly rendered2". + +5.9.042 (2011-01-14) + - Some problems of the PHP4 version were fixed. + +5.9.041 (2011-01-13) + - A problem with SVG elliptical arc path was fixed (ref. bug #3156574). + - A problem related to font weight on HTML table headers was fixed. + +5.9.040 (2011-01-12) + - A bug related to empty pages after table was fixed. + +5.9.039 (2011-01-12) + - Bug item #3155759 "openssl_random_pseudo_bytes() slow under Windows" was fixed. + +5.9.038 (2011-01-11) + - Minor bugs were fixed. + +5.9.037 (2011-01-09) + - An alignment problem for HTML texts was fixed. + +5.9.036 (2011-01-07) + - A bug related to HTML tables on header was fixed. + +5.9.035 (2011-01-03) + - A problem related to HTML table border alignment was fixed. + - Bug #2996366 "FastCGI and Header Problems" was fixed. + +5.9.034 (2010-12-19) + - DejaVu and GNU Free fonts were updated. + +5.9.033 (2010-12-18) + - Source code documetnation was improved. + +5.9.032 (2010-12-18) + - Default font stretching and spacing values are now inherited by HTML methods. + +5.9.031 (2010-12-16) + - Source code documentation errors were fixed. + +5.9.030 (2010-12-16) + - Several source code documentation errors were fixed. + - Source code style was changed for Doxygen. + - Source code documentation was moved online to http://www.tcpdf.org + +5.9.029 (2010-12-04) + - The $fitbox parameter on Image() method was extended to specify image alignment inside the box (check the example n. 9). + +5.9.028 (2010-12-03) + - Font utils makefont.php and makeallttffonts.php were updated. + +5.9.027 (2010-12-01) + - Spot Colors are now better integrated with HTML mode. + - Method SetDocInfoUnicode() was added to turn on/off Unicode mode for document information dictionary (meta tags) - check the example n. 19. + +5.9.026 (2010-12-01) + - A problem with mixed text directions on HTML was fixed. + +5.9.025 (2010-12-01) + - The AddSpotColor() now automatically fills the spotcolor array (defined on spotcolors.php file). + +5.9.024 (2010-11-30) + - Bug item #3123612 "SVG not use gradientTransform in percentage mode" was fixed. + +5.9.023 (2010-11-25) + - A potential bug on SVG transcoder was fixed. + +5.9.022 (2010-11-21) + - Method ImageEPS includes support for EPS/AI Spot colors. + - Method ImageEPS includes a new parameter $fixoutvals to remove values outside the bounding box. + +5.9.021 (2010-11-20) + - Support for custom bullet points images was added (check the example n.6) + - Examples n. 6 and 61 were update (check the comments inside). + +5.9.020 (2010-11-19) + - A problem related to additional page when using multicolumn mode was fixed. + +5.9.019 (2010-11-19) + - An SVG bug was fixed. + - ImageSVG() and ImageEPS() methods now accepts image data streams (put the string on the $file parameter preceded by '@' character). + - Option 'E' was added to the $dest parameter of Output() method to return the document as base64 mime multi-part email attachment (RFC 2045). + +5.9.018 (2010-11-19) + - An SVG bug was fixed. + +5.9.017 (2010-11-16) + - Tagline color was set to transparent. + - The method fixHTMLCode() was added to automatically clean up HTML code (requires HTML Tidy). + +5.9.016 (2010-11-16) + - Bug item #3109705 "list item page break hanging bullet" was fixed. + +5.9.015 (2010-11-16) + - Bug item affecting QRCode was fixed. + - Some bugs affecting HTML lists were fixed. + - ImageSVG() and fitBlock() methods were improved to handle some SVG problems. + - Some problems with PHP4 compatibility were fixed. + +5.9.014 (2010-11-15) + - Bug item #3109464 "QRCode error" was fixed. + +5.9.013 (2010-11-15) + - Bug item #3109257 "Problem with interlaced GIFs and PNGs" was fixed. + - Image function now accepts image data streams (check example n. 9). + +5.9.012 (2010-11-12) + - Method getTCPDFVersion() was added. + - PDF_PRODUCER constant was removed. + - Method convertHTMLColorToDec() was improved. + - HTML colors now support spot color names defined on the new spotcolors.php file. + - The default method Header() was improved to support SVG and EPS/AI images. + - A bug on SVG importer was fixed. + +5.9.011 (2010-11-02) + - Bug item #3101486 "Bug Fix for image loading" was fixed. + +5.9.010 (2010-10-27) + - Support for CSS properties 'border-spacing' and 'padding' for tables were added. + - Several language files were added. + +5.9.009 (2010-10-21) + - HTML text alignment was improved to include the case of RTL text on LTR direction and LTR text on RTL direction. + +5.9.008 (2010-10-21) + - Bug item #3091502 "Bookmark oddity" was fixed. + - HTML internal links now accepts page number and Y position. + - The method write1DBarcode() was improved to accept separate horizontal and vertical padding (see example n. 27). + +5.9.007 (2010-10-20) + - Method adjustCellPadding() was fixed to handle bad input. + +5.9.006 (2010-10-19) + - Support for AES 256 bit encryption was added (see example n. 16). + - Method getNumLines() was fixed for the empty string case. + +5.9.005 (2010-10-18) + - Method addPageRegion() was changed to accept regions starting exactly from the top of the page. + +5.9.004 (2010-10-18) + - A bug related to annotations was fixed. + - The file unicode_data.php was canged to encapsulate all data in a class. + - The file htmlcolors.php was changed to remove the global variable. + +5.9.003 (2010-10-15) + - Support for no-write page regions was added. Check the example n. 64 and new methods setPageRegions(), addPageRegion(), getPageRegions(), removePageRegion(). + - A bug on Right-To-Left alignment was fixed. + +5.9.002 (2010-10-08) + - Cell method was improved to preserve the font stretching and spacing values when using the $stretch parameter (see example n. 4). + +5.9.001 (2010-10-07) + - The problem of blank page for nobr table higher than a single page was fixed. + +5.9.000 (2010-10-06) + - Support for text stretching and spacing (kerning) was added, see example n. 63 and methods setFontStretching(), getFontStretching(), setFontSpacing(), getFontSpacing(). + - Support for CSS properties 'font-stretch' and 'letter-spacing' was added (see example n. 63). + - The cMargin state was replaced by cell_padding array that can be set/get using setCellPadding() and getCellPadding() methods. + - Methods getCellPaddings() and setCellPaddings() were added to fine tune cell paddings (see example n. 5). + - Methods getCellMargins() and setCellMargins() were added to fine tune cell margins (see example n. 5). + - Method write1DBarcode() was improved to permit custom labels (see example n. 27). + - Method ImagePngAlpha() now includes support for ImageMagick to improve performances. + - XObject Template support was extended to support Multicell(), writeHTML() and writeHTMLCell() methods. + - The signature of getNumLines() and getStringHeight() methods is changed. + - Example n. 57 was updated. + +// ------------------------------------------------------------------- + +5.8.034 (2010-09-27) + - A bug related to SetFont on XObject templates was fixed. + +5.8.033 (2010-09-25) + - A problem with Footer() and multiple columns was fixed. + +5.8.032 (2010-09-22) + - Bug #3073165 "Issues with changes to addHTMLVertSpace()" was fixed. + +5.8.031 (2010-09-20) + - Bug #3071961 "Spaces in HTML" was fixed. + +5.8.030 (2010-09-17) + - SVG support was improved and some bugs were fixed. + +5.8.029 (2010-09-16) + - A problem with HTML borders was fixed. + +5.8.028 (2010-09-13) + - Bug #3065224 "mcrypt_create_iv error on TCPDF 5.8.027 on PHP 5.3.2" was fixed. + +5.8.027 (2010-09-13) + - Bug #3065118 "mcrypt_decrypt error on TCPDF 5.8.026 on PHP 5.3.2" was fixed. + +5.8.026 (2010-09-13) + - A bug on addHTMLTOC() method was fixed. Note: be sure that the #TOC_PAGE_NUMBER# template has enough width to be printed correctly. + +5.8.025 (2010-09-09) + - Bug #3062692 "Textarea inside a table" was fixed. + +5.8.024 (2010-