URL of the string to extract from * @start_d Date of the first day * * @return instance Instance * ---------------------------------------------------------*/ public function __construct($img_url=null, $start_d=null){ /* [1] Check arguments =========================================================*/ { /* (1) Check type */ if( !is_string($img_url) || !is_string($start_d) ) throw new \Exception("CalendarExtractor.__construct(, ) expected but CalendarExtractor.__construct(<".gettype($img_url).">, <".gettype($start_d).">) received"); /* (2) Check @img_url link availability */ if( !($img_head=@get_headers($img_url)) ) throw new \Exception("CalendarExtractor.__construct(, ) received but cannot reach "); if( !preg_match('@HTTP.+200@', $img_head[0]) ) throw new \Exception("CalendarExtractor.__construct(, ) received but cannot reach "); /* (3) Check @start_d format */ if( !preg_match("@^\d{1,2}-\d{1,2}-\d{3,}$@", $start_d) ) throw new \Exception("CalendarExtractor.__construct(, ) received has not the correct format"); } /* [2] Fetch file =========================================================*/ { /* (1) Try to open file with GD */ $this->img_res = @imagecreatefrompng($img_url); /* (2) Manage error */ if( !$this->img_res ) throw new \Exception("URL is not a JPEG image, or is unreachable"); /* (3) Register data */ $this->img_url = $img_url; $this->start_d = $start_d; } } /* (2) Extracts calendar data from image * * @return error FALSE on error * ---------------------------------------------------------*/ public function process(){ /* [1] Global variables =========================================================*/ { /* (1) Image size */ { // {1} Request for size // $img_siz = @getimagesize($this->img_url); // {2} If error // if( !$img_siz ) throw new \Exception("Cannot get image size"); $img_siz = [ 'w' => $img_siz[0], 'h' => $img_siz[1] ]; } } /* [2] Extract day limits =========================================================*/ { /* (1) Will contain the column+1 w index */ $col_ind = []; /* (2) Extract column indexes */ for( $x = 0 ; $x < $img_siz['w'] ; $x++ ){ if( $this->getColor($x, 62) <= 10 ) $col_ind[] = $x+1; } /* (3) Remove last */ unset( $col_ind[count($col_ind)-1] ); } /* [3] For each day -> get events =========================================================*/ { /* (1) For each day */ foreach($col_ind as $day_n=>$col_x){ /* (2) For each y pixel -> exctract event */ for( $y = self::$start_y ; $y < self::$stop_y ; $y++ ){ /* (3) Get current color + next */ $p = $this->getColor($col_x, $y); $p1 = $this->getColor($col_x, $y+1); /* (4) If on black pixel and next not white */ if( $p == 0 && $p1 != 0xffffff ){ // {1} calculate time // $time = $this->yToTime($day_n, $y); // {2} Store new event type (if missing) // if( !isset($this->event_t[$p1]) ) $this->event_t[$p1] = $this->event_t[0]++; $uid = $this->event_t[$p1]; // {3} Store event start // $this->event[$uid][$time] = []; // {4} Seek end of event // $y++; while( $y < self::$stop_y && $this->getColor($col_x, $y) != 0 ) $y++; // {5} If end reached // $this->event[$uid][$time] = $this->yToTime($day_n, $y); } } } } } /* (3) Get a pixel's color * * @x X coordinate * @y Y coordinate * * @return color Raw color * ---------------------------------------------------------*/ private function getColor($x, $y){ return imagecolorat($this->img_res, $x, $y); } /* (4) Get time from a y-coordinate * * @day_n Day relative index * @y y Coordinate * * @return time Formatted time (HH:mm) * ---------------------------------------------------------*/ private function yToTime($day_n, $y){ /* [1] Get the date =========================================================*/ { /* (1) Get the day's date */ $day_ts = strtotime($this->start_d." + $day_n days"); /* (2) Format it */ $day = date('Ymd', $day_ts); } /* [2] Get the time =========================================================*/{ /* (1) Get the time */ $time = self::$start_h + (self::$stop_h-self::$start_h) * ($y-self::$start_y) / (self::$stop_y-self::$start_y); /* (2) Calculate hour form time */ $hour = floor($time); /* (3) Calculate min from time */ $min = round( 60 * ($time-$hour) ); /* (4) Round minutes to 10min */ $min = round($min/10)*10; /* (5) Format to 2-digit */ $hour = ( $hour < 10 ) ? "0$hour" : $hour; $min = ( $min < 10 ) ? "0$min" : $min ; } /* [3] Convert to GMT (UTC+0) =========================================================*/ /* (1) Set fixed timezone offset */ $tz_offset = +2; /* (2) Get GMT (UTC+0) timestamp */ $ts = strtotime("${day} $hour:$min:00") + 3600*(-$tz_offset); /* (3) Return GMT date */ return date("Ymd\THis", $ts); } /* (5) Generate ICS output * * @return ics ICS Representation of the events * ---------------------------------------------------------*/ public function toIcs(){ $RAW = ""; /* [1] Manage types =========================================================*/ $type_name = []; foreach($this->event_t as $color=>$uid) $type_name[$uid] = $color; /* [2] For each event =========================================================*/ foreach($this->event as $event_t=>$events){ $type = "Event of type '".$type_name[$event_t]."'"; /* (2) For each event of each type ---------------------------------------------------------*/ foreach($events as $start_t=>$stop_t){ $RAW .= "BEGIN:VEVENT\n"; $RAW .= "UID:$start_t@example.com\n"; // required $RAW .= "DTSTAMP:${start_t}Z\n"; // required $RAW .= "DTSTART:${start_t}Z\n"; $RAW .= "DTEND:${stop_t}Z\n"; $RAW .= "SUMMARY:$type\n"; $RAW .= "END:VEVENT\n\n"; } } return $RAW; } }