"FusionCharts", "exportaction" => "download", "exporttargetwindow" => "_self", "exportformat" => "PNG" ); // Stores server notices if any as string [ to be send back to chart after save ] $notices = ""; // ============================================================================= // == processing == // ============================================================================= /** * Retrieve export data from POST Request sent by chart * Parse the Request stream into export data readable by this script * * Store export data into an array containing keys 'stream' (contains encoded * image data) ; 'meta' ( contains an array with 'width', 'height' and 'bgColor' keys) ; * and 'parameters' ( array of all export parameters from chart as keys, like - exportFormat, * exportFileName, exportAction etc.) */ $exportRequestStream = $_POST; //echo "
"; // print_r($exportRequestStream); //die(); //echo ""; // InsertToDB // Uncomment the below line if want to save the export log in database //insertToDb($exportRequestStream); $exportData = parseExportRequestStream($exportRequestStream); function convertRawImageDataToFile($exportData) { $mimeTypeArray = array("jpg=image/jpeg", "jpeg=image/jpeg", "gif=image/gif", "png=image/png", "pdf=application/pdf", "svg=image/svg+xml"); $mime = ""; foreach($mimeTypeArray as $mime) { if(strpos($mime, strtolower($exportData['parameters']['exportformat']))!==false) { break; } } if (strtolower($exportData['parameters']['exportaction']) === 'save') { $fileStatus = setupServer($exportData['parameters']['exportfilename'], strtolower($exportData['parameters']['exportformat']), $target = "_self"); print_r($fileStatus['filepath']); if ($fileStatus ['ready']) { file_put_contents($fileStatus['filepath'], $exportData['stream']); } } else { header('Content-type:' . $mime); header('Content-Disposition: attachment; filename="' . $exportData["parameters"]["exportfilename"].'.'.strtolower($exportData["parameters"]["exportformat"]) . '"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); ob_clean(); ob_end_flush(); print_r($exportData['stream']); } exit; } if($exportData['streamtype']==="IMAGE-DATA") { $exportObject = convertRawImageDataToFile($exportData); } /** * If encoded images are found we need to decode and save them in the temp folder. */ if($exportData ['encodedImageData'] && strtolower($exportData ['parameters']["exportformat"]) != 'svg' ){ //createEmbeddedImages($exportData ['encodedImageData']); parseImageData( $exportData ['encodedImageData']); } /** * Fix for Issue with safari when exported as pdf stroke-width="0.0001" is not supporting by inkscape */ if(strtolower($exportData ['parameters']["exportformat"]) == 'pdf') { $exportData ['stream'] = preg_replace('/stroke-width\s*=\s*["\']0\.[^"\']*"/i', 'stroke-width="0"', $exportData ['stream']); } /** * Get the name of the export resource (php file) as per export format * Dynamically include the resource. The resource would process the data * and perform all export related tasks */ $exporterResource = getExporter($exportData ['parameters'] ["exportformat"], $exportData ["streamtype"]); // if resource is not found terminate with error report if (!@include( $exporterResource )) { raise_error(404, true); } /* * Pass export stream and meta values to the export processor & * get back the export binary */ $exportObject = exportProcessor($exportData ['stream'], $exportData ['meta'], $exportData ['parameters'], $exportData ['encodedImageData']); /* * Send the export binary to output module which would either save to a server directory * or send the export file to download. Download terminates the process while * after save the output module sends back export status */ $exportedStatus = outputExportObject($exportObject, $exportData ['parameters']); /* * Build Appropriate Export Status and send back to chart by flushing the * procesed status to http response. This returns status back to chart. * [ This is not applicable when Download action took place ] */ flushStatus($exportedStatus, $exportData ['meta']); // ============================================================================= // == terminate process == // ============================================================================= ################################################################################# ## ## ## FUNCTION DECLARATION ## ## ## ################################################################################# #### ------------------------ INPUT STREAM -------------------------------- #### /** * Parses POST stream from chart and builds an array containing * export data and parameters in a format readable by other functions. * * @param $exportRequestStream All POST data (array) from chart * @return An array of processed export data and parameters */ function parseExportRequestStream($exportRequestStream) { // Check for SVG $exportData ['streamtype'] = strtoupper(@$exportRequestStream ['stream_type']); // backward compatible SVG stream type detection if (!$exportData ['streamtype']) { if (@$exportRequestStream ['svg']) { $exportData ['streamtype'] = "SVG"; } else { $exportData ['streamtype'] = "RLE"; } } // get string of compressed/encoded image data // halt with error message if stream is not found if( strtolower($exportData ['streamtype']) === "svg") { $exportData ['stream'] = (string)@$exportRequestStream ['stream'] or $exportData ['stream'] = (string)@$exportRequestStream ['svg'] // backward compatible or raise_error(100, true); } else { if(!isset($exportRequestStream ['stream'])) { raise_error(100, true); } // if stream-type is not SVG then this section will execute $exportData ['stream'] = (str_replace(' ','+', $exportRequestStream ['stream'])); $exportData ['stream'] = base64_decode(substr($exportData ['stream'], strpos($exportData ['stream'], ",")+1)); } // get all export related parameters and parse to validate and process these // add notice if 'parameters' is not retrieved. In that case default values would be taken if (!@$exportRequestStream['parameters']) raise_error(102); // parse parameters $exportData ['parameters'] = parseExportParams(@$exportRequestStream ['parameters'], @$exportRequestStream); $exportData ['parameters'] ["exportformat"] = strtoupper(@$exportData ['parameters'] ["exportformat"]); // get width and height of the chart // halt with error message if width/height is/are not retrieved $exportData ['meta']['width'] = (int) @$exportRequestStream ['meta_width'] or $exportData ['meta']['width'] = (int) @$exportRequestStream ['width'] // backward compatible or raise_error(101); $exportData ['meta']['height'] = (int) @$exportRequestStream ['meta_height'] or raise_error(101); // get background color of chart // add notice if background color is not retrieved $exportData ['meta']['bgColor'] = @$exportRequestStream ['meta_bgColor']; // chart DOMId $exportData ['meta']['DOMId'] = @$exportRequestStream ['meta_DOMId']; // get the encoded images if any And temporarily create them inside the the temp // folder $exportData ['encodedImageData'] = @$exportRequestStream ['encodedImgData']; // return collected and processed data return $exportData; } /** * Parse export 'parameters' string into an associative array with key => value elements. * Also sync default values from $defaultparameterValues array (global) * @param $strParams A string with parameters (key=value pairs) separated by | (pipe) * @return An associative array of key => value pairs */ function parseExportParams($strParams, $exportRequestStream = array()) { // get global definition of default parameter values global $defaultParameterValues; // split string into associative array of [export parameter name => value ] $params = bang($strParams, array("|", "=")); $exportFilename = @$params['exportfilename']; $exportFormat = @$params['exportformat']; // backward compatible setting to get filename if (!$exportFilename) { $exportFilename = (string)@$exportRequestStream["filename"]; if ($exportFilename) { $params['exportfilename'] = $exportFilename; } } // backward compatible setting to get exportFormat through mimetype if (!$exportFormat) { $mimeType = strtolower((string)@$exportRequestStream["type"]); $mimeList = bang( @MIME_TO_FORMAT ); $exportFormat = $mimeList[$mimeType]; if ($exportFormat) { $params['exportformat'] = $exportFormat; } else { $params['exportformat'] = 'png'; } } if (is_array($defaultParameterValues)) { // sync with default values $params = $params + $defaultParameterValues; } // return parameters' array return $params; } /** * parseImageData function parses the JSON formatted encoded image data * and its associated attributes. It then decodes the base64 image string * and stores in an array for image re-creation at server. * @param $dataStr JSON formatted image data and its attrbutes. * */ function parseImageData($dataStr){ $dataObj = json_decode($dataStr); $images_to_save = array(); foreach ($dataObj as $key => $value) { $image = $value; foreach ($image as $key => $value) { switch(strtolower($key)) { case 'name': $img_name = $value; break; case 'type': $img_type = $value; break; case 'encodeddata': $img_data = $value; break; case 'width': $img_width = $value; break; case 'height': $img_height = $value; break; default: #nothing to do break; } } $img_data = str_replace('data:image/'.$img_type.";base64,", '', $img_data); $img_data = str_replace(' ', '+', $img_data); $img_data = base64_decode($img_data); if(!$img_data){ raise_error("Problem Decoding base64 String"); } $img_obj = (object)array( 'name' => $img_name, 'data' => $img_data, 'type' => $img_type ); array_push($images_to_save, $img_obj); } if(count($images_to_save) > 0) { saveNextImage($images_to_save, 0); } } /** * saveNextImage function sequentially gets the image data and * send for recreating the images. * @param [type] $images [description] * @param [type] $counter [description] * @return [type] [description] */ function saveNextImage($images, $counter) { if(isset($images[$counter])){ $img_name = $images[$counter]->name; $img_type = $images[$counter]->type; $img_data = $images[$counter]->data; /* Even if image creation is very fast (Synchronous), we should wait for the completion of one process, before invoking the other. Hence, the following if and else is required. */ if(saveEmbeddedImage($img_name, $img_type, $img_data, "temp")) { /* For successful creation of the images moves to the next image*/ $counter++; saveNextImage($images, $counter); } else { /*and for failure raise an error and then check for the next image. */ raise_error("can not create file ". $img_name. "." .$img); $counter++; saveNextImage($images, $counter); }; } } /** * saveEmbeddedImage takes all the required parameters to recreate the * encoded image at server. * @param $name the name of the image to be recreated * @param $type The type of the image to be recreated * @param $data The decoded binary data to save as the image * @param $path The relative path of the folder where the * image should be recreated. This is normally * the 'temp' folder. * * @return Returns true for successful recreaion and false * for failure. */ function saveEmbeddedImage($name, $type, $data, $path="temp"){ $resource = imagecreatefromstring($data); $image_output_path = realpath($path) . "/" . $name . "." . $type; if(!$resource){ raise_error("Image resource could not be created for " . $name); }else{ if($type == 'png'){ imagealphablending($resource, false); imagesavealpha($resource, true); return imagepng($resource, $image_output_path, 9); }else{ return imagejpeg($resource, $image_output_path, 100); } } //Should free up memory. imagedestroy($resource); } /** * Builds and returns a path of the Export Resource PHP file needed to * export the chart to the format specified as parameter. * @param $strFormat (string) export format specified form chart * @return A path (string) containing the Export Resource PHP file * for the specified format */ function getExporter($strFormat, $streamtype = "RLE") { // get array of [format => handler suffix ] from HANDLER_ASSOCIATIONS $associationCluster = bang(HANDLER_ASSOCIATIONS, array('|', ':'), true); $associations = bang(@$associationCluster[$streamtype], array(";", "="), true); // validate and decide on proper suffix form the $associations array // if not found take the format as suffix of the Export Resource $exporterSuffix = (@$associations [$strFormat]); if (!$exporterSuffix) { $exporterSuffix = strtoupper($strFormat); } // build Export Resource PHP file path // Add resource path (constant), Export handler (constant) and export suffix $path = RESOURCE_PATH . EXPORT_HANDLER . strtoupper($streamtype) . "2{$exporterSuffix}.php"; return $path; } #### ------------------------ OUTPUT EXPORT FILE -------------------------------- #### /** * Checks whether the export action is download or save. * If action is 'download', send export parameters to 'setupDownload' function. * If action is not-'download', send export parameters to 'setupServer' function. * In either case it gets exportSettings and passes the settings along with * processed export binary (image/PDF) to the output handler function if the * export settings return a 'ready' flag set to 'true' or 'download'. The export * process would stop here if the action is 'download'. In the other case, * it gets back success status from output handler function and returns it. * * @param $exportObj An export binary/object of mixed type (image/PDF) * @param $exportParams An array of export parameters * @return export success status ( filename if success, false if not) */ function outputExportObject($exportObj, $exportParams) { // checks whether the export action is 'download' $isDownload = strtolower($exportParams ["exportaction"]) == "download"; // dynamically call 'setupDownload' or 'setupServer' as per export action // pass export paramters and get back export settings in an array $exportActionSettings = call_user_func('setup' . ($isDownload ? 'Download' : 'Server'), $exportParams['exportfilename'], $exportParams['exportformat'], $exportParams['exporttargetwindow']); // check whether export setting gives a 'ready' flag to true/'download' // and call output handler // return status back (filename if success, false if not success ) return ( @$exportActionSettings ['ready'] ? exportOutput($exportObj, $exportActionSettings, 1) : false ); } /** * Flushes exported status message/or any status message to the chart or the output stream on error * It parses the exported status through parser function parseExportedStatus, * builds proper response string using buildResponse function and flushes the response * string to the output stream and terminates the program. * @param $status exported status ( false if failed/error, filename as string if success) * $meta array containing meta descriptions of the chart like width, height * $msg custom message to be added as statusMessage * */ function flushStatus($status, $meta, $msg = '') { die(buildResponse(parseExportedStatus($status, $meta, $msg))); } /** * Parses the exported status and builds an array of export status information. As per * status it builds a status array which contains statusCode (0/1), statusMesage, fileName, * width, height, DOMId and notice in some cases. * @param $status exported status ( false if failed/error, filename as stirng if success) * $meta array containing meta descriptions of the chart like width, height and DOMId * $msg custom message to be added as statusMessage * @return array of status information */ function parseExportedStatus($status, $meta, $msg = '') { // get global 'notice' variable global $notices; global $exportData; // add notice if ($notices) $arrStatus [] = "notice=" . @$notices; // Add DOMId $arrStatus [] = "DOMId=" . @$meta["DOMId"]; // add file URI , width and height when status success // provide 0 as width and height on failure $arrStatus [] = "height=" . ( @$status ? @$meta ['height'] : 0 ); $arrStatus [] = "width=" . ( @$status ? @$meta ['width'] : 0 ); $arrStatus [] = "fileName=" . ( @$status ? ( preg_replace('/([^\/]$)/i', '${1}/', HTTP_URI) . @$status ) : "" ); // add status message . Priority 1 is a custom message if provided $arrStatus [] = "statusMessage=" . ( trim(@$msg) ? @$msg : ( $status ? "success" : "failure" )); // add statusCode to 1 on success $arrStatus [] = "statusCode=" . ( @$status ? "1" : "0" ); // return status information return $arrStatus; } /** * Builds response from an array of status information. Each value of the array * should be a string which is one [key=value ] pair. This array are either joined by * a & to build a querystring (to pass to chart) or joined by a HTML