Wednesday, January 18, 2012

Flash AS3 + PHP: Publishing your flash contents into a PDF file (2)

Last week, I have shown you how to create a downloadable PDF using Flex + PHP + the Alive PDF library. As for this week, I shall move on to the step of printing out & breaking up a big datagrid into multiple pages.

The following would be the source file of my Main Application in Flex.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
    creationComplete="creationCompleteHandler(event)">
 <mx:Script>
  <![CDATA[
   //Import the classes that are needed.
   import com.extentPDF.ExtendingPDF;
   
   import mx.collections.ArrayCollection;
   import mx.controls.dataGridClasses.DataGridColumn;
   import mx.events.FlexEvent;
   
   //Array to store the random data that I'm going to create
   private var dataArray:Array = new Array();
   private var dataColArray:ArrayCollection = new ArrayCollection();
   
   //Array to store the column sequence
   private var dgColumns:Array = new Array();
   
   protected function creationCompleteHandler(event:FlexEvent):void
   {
    //Add the respective column names into the array.
    dgColumns.push(new DataGridColumn("Name"));
    dgColumns.push(new DataGridColumn("Description"));
    dgColumns.push(new DataGridColumn("Price"));
    dgColumns.push(new DataGridColumn("Unit"));
    //Set the columns of the datagrid.
    dgDisplay.columns = dgColumns;
    
    //Create random data
    var j:int = 0;
    var tempObject:Object;
    for(var i:int = 1; i <= 100; i ++)
    {
     tempObject = new Object();
     if(i % 2 == 1)
     {
      tempObject.Name = "Item " + i;
     }else{
      tempObject.Name = "Product " + i;
     }
     tempObject.Price = "S$" + i;
     j = Math.round(Math.random() * 100);
     tempObject.Unit = i * j;
     tempObject.Description = "Description " + i;
     dataArray.push(tempObject);
    }
    //Pass the data into the datagrid
    dataColArray.source = dataArray;
    dgDisplay.dataProvider = dataColArray;
   }
   
   //Upon clicking on the button, make a downloadable pdf.
   protected function clickHandler(event:MouseEvent):void
   {
    saveContent();
   }  
   
   //Function that will be creating the downloadable PDF
   //with the help of PHP
   private function saveContent():void
   {
    //Create a new instance of ExtendingPDF class.
    var tempPDF:ExtendingPDF = new ExtendingPDF();
    //Add it to this application.
    this.addChild(tempPDF);
    this.validateNow();
    
    //Specify the first row that you are displaying
    var start:int = 0;
    //Specify the last row that you are displaying
    var end:int = 1;
    //Used to specify the row number of the data
    var row:int = 0;
    //Array to create a reference to the 'dataArray'
    //based on the start and the end
    var dummyArray:Array;
    //Array to store the result of the check upon
    //the PDF
    var checkResult:Array = null;
    //Should be add the last known good view to the PDF? 
    var mustInsertPrev:Boolean = false;
    //Loop through all the rows of the 'dataArray'
    //Add the view into the PDF if the view can fit in
    //else keep on adding a new row until it reach a point
    //where the new row cannot be added and then add the 
    //last known good view into the PDF.
    while(end <= dataArray.length)
    {
     dummyArray = new Array();
     row = 0;
     for(var i:int = start; i < end; i ++)
     {
      dummyArray[row] = dataArray[i];
      row ++;
     }
     dataColArray.source = dummyArray;
     dgDisplay.dataProvider = dataColArray;
     dgDisplay.verticalScrollPolicy = "off";
     dgDisplay.height = dgDisplay.headerHeight +
      dgDisplay.measureHeightOfItems(start, end - start);
     dgDisplay.validateNow();
     mustInsertPrev = false;
     checkResult = tempPDF.checkContentSize2PDFPage(dgDisplay);
     if(checkResult[0] == 1)
     {
      if((start + 1) == end)
      {
       tempPDF.addPage();
       checkResult = tempPDF.checkContentSize2PDFPage(dgDisplay);
       if(checkResult[0] == 0)
       {
        mustInsertPrev = false;
       }else{
        mustInsertPrev = true;
       }
      }else{
       mustInsertPrev = true;
      }
      if(mustInsertPrev)
      {
       end --;
       dummyArray = new Array();
       row = 0;
       for(i = start; i < end; i ++)
       {
        dummyArray[row] = dataArray[i];
        row ++;
       }
       dataColArray.source = dummyArray;
       dgDisplay.dataProvider = dataColArray;
       dgDisplay.verticalScrollPolicy = "off";
       dgDisplay.height = dgDisplay.headerHeight +
        dgDisplay.measureHeightOfItems(start, end - start);
       dgDisplay.validateNow();
       tempPDF.addContent2PDFPage(dgDisplay);
       start = end;
       end ++;
      }
     }else{
      end ++;
      if(end > dataArray.length)
      {
       tempPDF.addContent2PDFPage(dgDisplay);
      }
     }
    }
    //Create the PDF file that can be downloaded
    tempPDF.createContent();
    //Restore everything back to the state before
    //the PDF was created.
    dataColArray.source = dataArray;
    dgDisplay.dataProvider = dataColArray;
    dgDisplay.verticalScrollPolicy = "auto";
    dgDisplay.height = 200;
    container.addChildAt(dgDisplay,0);
    this.removeChild(tempPDF);
    this.validateNow();
   }
   
  ]]>
 </mx:Script>
 <mx:VBox id="container" width="100%" height="100%" 
    horizontalAlign="center" verticalAlign="middle">
  <mx:DataGrid id="dgDisplay" height="200"/>
  <mx:Button label="Print Content Now!" click="clickHandler(event)"/>
 </mx:VBox>
</mx:Application>

A Custom Class that I had created to handle the printing...
package com.extentPDF
{
 import flash.display.DisplayObject;
 import flash.geom.Point;
 
 import mx.containers.Canvas;
 
 import org.alivepdf.display.Display;
 import org.alivepdf.layout.Layout;
 import org.alivepdf.layout.Mode;
 import org.alivepdf.layout.Orientation;
 import org.alivepdf.layout.Position;
 import org.alivepdf.layout.Resize;
 import org.alivepdf.layout.Size;
 import org.alivepdf.layout.Unit;
 import org.alivepdf.pages.Page;
 import org.alivepdf.pdf.PDF;
 import org.alivepdf.saving.Download;
 import org.alivepdf.saving.Method;
 
 public class ExtendingPDF extends Canvas
 {
  //Path to the Php file used to generate the pdf file
  private var phpUrl:String = "http://bestkirdape.freeiz.com/php/create.php";
  
  //File Name that I'm giving the pdf file
  private var strFilename:String = "PrintOut_Of_MultipleViewWithAlivePDF.pdf";
  
  private var myPDF:PDF;
  private var pdfHeight:int = 0;
  
  public function ExtendingPDF()
  {
   //Specify the settings of the PDF
   //In my case, I'm using a PORTRAIT view, a size of A4
   //and everything will be calculated in POINT.
   myPDF = new PDF(Orientation.PORTRAIT, 
    Unit.POINT, Size.A4);
   //Set the Display Mode of the PDF when it was opened.
   myPDF.setDisplayMode (Display.FULL_PAGE, Layout.SINGLE_PAGE);
   //Set the Margins (left, top, right, bottom)
   myPDF.setMargins(40,60,40,60);
   addPage();
  }
  
  //Create a new page into the PDF.
  public function addPage():void
  {
   var newPage:Page = new Page ( Orientation.PORTRAIT, 
    Unit.POINT, Size.A4 );
   //Add a Page to the PDF
   myPDF.addPage(newPage);
   pdfHeight = 0;
  }
  
  //This function is used to add the content into the current page
  public function addContent2PDFPage(content:DisplayObject):void
  {
   var resultArray:Array = checkContentSize2PDFPage(content);
   //Check if the content can fit into the current page
   //else need to create a new page
   if (resultArray[0] == 1)
   {
    addPage();
   }
   //Add the content into a Canvas first before adding into
   //the current page of the pdf.
   var tempContainer:Canvas = new Canvas();
   this.addChild(tempContainer);
   tempContainer.width = Math.floor(content.width) + 4;
   tempContainer.height = Math.floor(content.height) + 4;
   tempContainer.setStyle("backgroundAlpha",0);
   tempContainer.addChild(content);
   content.x = 0;
   content.y = 0;
   tempContainer.validateNow();
   this.validateNow();
   
   //Adds like a screen capture and capture a screen shot
   //of the content
   myPDF.addImage(tempContainer, new Resize(Mode.NONE, 
    Position.LEFT), 0, pdfHeight, (resultArray[1] as Point).x, 
    (resultArray[1] as Point).y);
   pdfHeight = pdfHeight + (resultArray[1] as Point).y + 10;
   
   //remove views that are not needed
   this.removeChild(tempContainer);
  }
  
  //This function is used to check whether the content 
  //can fit into the current page
  public function checkContentSize2PDFPage(content:DisplayObject):Array
  {
   var pageWidth:Number = myPDF.getCurrentPage().w - 80;
   var pageHeight:Number = myPDF.getCurrentPage().h - 120;
   var imgScaleFactor:Number = content.width/content.height;
   
   var pw:int = 0;
   var ph:int = 0;
   pw = Math.floor(pageWidth);
   ph = Math.floor(pw/imgScaleFactor);
   var tempArray:Array = new Array();
   var tempPoint:Point = new Point();
   tempPoint.x = pw;
   tempPoint.y = ph;
   
   if ((pdfHeight + ph) > pageHeight)
   {
    tempArray[0] = 1;
   }else{
    tempArray[0] = 0;
   }
   tempArray[1] = tempPoint;
   return tempArray;
  }  
  
  //Create the PDF file that can be downloaded
  public function createContent():void
  {
   myPDF.save( Method.REMOTE, phpUrl, Download.ATTACHMENT, strFilename);
  }   
 }
}

* Click here to view the demo of this example.
^ Click here for the source files of this demo.

~ Click here for the website of 'Alive PDF'.
- Click here for the Documentation of the classes of 'Alive PDF'.
** Click here for the older tutorials on 'Alive PDF'.

Sunday, January 15, 2012

Flash AS3 + PHP: Publishing your flash contents into a PDF file

With the release of Flash Player 10, you can easily create an image file to display all the contents into an image on the fly. But with the help of 'Alive PDF' and Php, you can actually create PDF on the fly.

The following would be a simple demo that I have created. The generated PDF will display 2 blocks of text. The first text would be a capture of the text view in the Flex Application and the second text was inserted into the PDF using the functions provided in the 'Alive PDF' API.

So let us take a look at some coding. :D
<?php

$method = $_GET['method'];
$name = $_GET['name'];

if ( isset ( $GLOBALS["HTTP_RAW_POST_DATA"] )) {
 
 // get bytearray
 $pdf = $GLOBALS["HTTP_RAW_POST_DATA"];
 
 // add headers for download dialog-box
 header('Content-Type: application/pdf');
 header('Content-Length: '.strlen($pdf));
 header('Content-disposition:'.$method.'; filename="'.$name.'"');
 echo $pdf;
 
}  else echo 'An error occured.';

?>
The above Php script was taken from the Alive PDF website.

The following would be the source file of my Main Application in Flex.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
    creationComplete="creationCompleteHandler(event)"
    width="100%" height="100%">
 <mx:Script>
  <![CDATA[
   import mx.events.FlexEvent;
   
   import org.alivepdf.colors.RGBColor;
   import org.alivepdf.display.Display;
   import org.alivepdf.fonts.CodePage;
   import org.alivepdf.fonts.EmbeddedFont;
   import org.alivepdf.layout.Layout;
   import org.alivepdf.layout.Mode;
   import org.alivepdf.layout.Orientation;
   import org.alivepdf.layout.Position;
   import org.alivepdf.layout.Resize;
   import org.alivepdf.layout.Size;
   import org.alivepdf.layout.Unit;
   import org.alivepdf.pages.Page;
   import org.alivepdf.pdf.PDF;
   import org.alivepdf.saving.Method;
   
   //Some Dummy Text
   [Bindable]
   private var msg:String = "Lorem ipsum dolor sit amet, " +
    "consectetur adipiscing elit. Cras nec eros sit amet " +
    "dui sagittis volutpat pharetra et est. Curabitur " +
    "ultricies suscipit volutpat. Aenean feugiat " +
    "ullamcorper pellentesque. Nulla ut venenatis ligula. " +
    "Nam ornare est a odio condimentum bibendum. Sed " +
    "lorem est, tristique at gravida in, blandit vitae leo. " +
    "Etiam lobortis mi vel magna porta tempus. Suspendisse " +
    "in tortor non magna hendrerit vulputate id quis justo. " +
    "Integer vehicula tortor tortor. Cras consequat, tortor " +
    "in cursus consequat, risus justo vehicula arcu, eu " +
    "consequat dolor leo sed felis. In euismod mollis blandit. " +
    "Vivamus tempus rutrum lorem quis commodo. Suspendisse " +
    "condimentum lorem at risus eleifend porttitor. Nulla " +
    "molestie ipsum nec lacus convallis semper sed ut enim. " +
    "Curabitur arcu justo, imperdiet ac rutrum eget, porta " +
    "eget orci. Nulla aliquet tristique rutrum.";
   
   //Path to the Php file used to generate the pdf file
   private var phpUrl:String = "http://bestkirdape.freeiz.com/php/create.php";
   
   //File Name that I'm giving the pdf file
   private var strFilename:String = "PrintOut_Of_PlayingWithAlivePDF.pdf";
   
   //Specify the Embedded font used by the label, lblText
   [Embed( source="fonts/jokerman.TTF", mimeType="application/x-font", fontName="jokermanFont")]
   public var jokermanFont:Class;
   
   //Specify the Embedded font that I'm using in the print out
   [Embed( source="fonts/jokerman.TTF", mimeType="application/octet-stream")]
   public var jokerman_ttf:Class;
   [Embed( source="fonts/jokerman.afm", mimeType="application/octet-stream" )]
   public var jokerman_afm:Class;
   
   //Specify the Font Color
   private var textColor:uint = 0x000000;
   
   //Open up the pdf upon clicking on the button 'Print Content Now!'.
   protected function btnPrint_clickHandler(event:MouseEvent):void
   {
    printContent();
   }
   
   private function printContent():void
   {
    //This variable is needed to specify the Embedded font that
    //we are using
    var font:EmbeddedFont = new EmbeddedFont(
     new jokerman_ttf() as ByteArray
     , new jokerman_afm() as ByteArray
     , CodePage.CP1252
    );
    //Specify the settings of the PDF
    //In my case, I'm using a PORTRAIT view, a size of A4
    //and everything will be calculated in POINT.
    var myPDF:PDF = new PDF(Orientation.PORTRAIT, 
     Unit.POINT, Size.A4);
    //Set the Display Mode of the PDF when it was opened.
    myPDF.setDisplayMode ( Display.FULL_WIDTH );
    //Set the Margins (left, top, right, bottom)
    myPDF.setMargins(10,20,10,20);
    //Add a Page to the PDF
    myPDF.addPage();
    //Change the size of the view,, lblText and ...    
    lblText.width = (myPDF.getCurrentPage().w - 20);
    resizeMe(lblText);
    lblText.validateNow();
    //add it to the PDF
    myPDF.addImage(lblText,null,0,0); 
    //Set the Font of the PDF
    myPDF.setFont(font, lblText.getStyle("fontSize"));
    //Set the Color of the Text of the PDF
    myPDF.textStyle(new RGBColor(Number(textColor)));
    //Change the XY coordinates of the view that
    //I'm going to add
    myPDF.setXY(10, lblText.height + 20 + 10);
    //Create a Cell to display my text and centralised it
    myPDF.addMultiCell(myPDF.getMargins().width,
     lblText.getStyle("fontSize") + 6, msg, 0, "C", 0);
    //*********UPDATED***********
    //Download the PDF file
    //Need to add Download.ATTACHMENT as one of the parameters 
    //in order to download the pdf file.
    myPDF.save( Method.REMOTE, phpUrl, Download.ATTACHMENT, strFilename);
    //Revert the width of the view, lblText.
    lblText.width = 400;
    resizeMe(lblText);
    lblText.validateNow();
   }
   
   protected function creationCompleteHandler(event:FlexEvent):void
   {
    resizeMe(lblText);
   }
   
   //Used to resize the TextArea so that all the text
   //will be shown.
   private function resizeMe(field:TextArea) :void
   {
    field.validateNow();
    field.mx_internal::getTextField().autoSize = 
     TextFieldAutoSize.CENTER;
    field.height = field.mx_internal::getTextField().height;
   }   
   
  ]]>
 </mx:Script>
 <mx:VBox width="100%" verticalAlign="middle" 
    horizontalAlign="center" height="100%">
  <mx:TextArea id="lblText" wordWrap="true"
      backgroundAlpha="0"
      borderThickness="0"
      fontFamily="jokermanFont"
      color="{textColor}"
      text="{msg}" width="400" textAlign="center"/>
  <mx:Button click="btnPrint_clickHandler(event)"
       label="Print Content Now!"/>
 </mx:VBox>
</mx:Application>

* Click here to view the demo of this example.
^ Click here for the source files of this demo.

~ Click here for the website of 'Alive PDF'.
- Click here for the Documentation of the classes of 'Alive PDF'.
** Click here for the .adm file generator.
(Note: you need to upload a .ttf file before you can generate the .adm file.

Monday, January 2, 2012

A stupid font settings of Windows 7

Have you encounter the problem of starting up your Windows 7 and realised that all your Chinese or Korean or Japanese characters have turned into square boxes like the following, []? I have encounter it before and today I am going to share a possible fix for it.

Here are the steps:
  1. Click on the 'Start' button on the bottom left of the screen
  2. Click on 'Control Panel'
  3. Click on 'Appearance and Personalization'
  4. Click on 'Fonts'
  5. In the left pane, click on 'Font settings'
  6. Click on 'Control Panel'
  7. Make sure that the 'Hide fonts based on language settings' check box is not selected
  8. Restart your Laptop or PC and you are done.

* Click here to find out more.

Sunday, January 1, 2012

Flex: Converting your views into ByteArray

Probably they are times where you need to send image captures of the views in the Flex Application to one of your servers but how do you do that? As for this week, I am going to show you how to convert all your views into ByteArray before you convert them into base64 strings and send it to the server.

The following would be the source code for my demo.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
    creationComplete="creationCompleteHandler(event)" 
    width="100%" height="100%">
 <mx:Script>
  <![CDATA[
   import mx.events.FlexEvent;
   //Need to import this class for capturing the view
   import mx.graphics.ImageSnapshot;
   
   //Array to stroe the data for the data grid
   private var arr:Array = new Array();
   private var arr1:Array = new Array();
   
   //Array to store data for the combobox
   private var arr_Content:Array = new Array();
   
   //ByteArray to store the captured image
   private var resultArray:ByteArray;
   
   //Upon clicking on the 'Main' button, 
   //change to the original state
   protected function btnMain_clickHandler(event:MouseEvent):void
   {
    this.currentState = "";
   }
   
   //Upon clicking on the 'Result' button, 
   //change to the 'result' view with the result Image
   protected function btnResult_clickHandler(event:MouseEvent):void
   {
    this.currentState = "result";
   }
   
   //Upon clicking on the 'Capture Now!' button, 
   //Capture the current look and feel of the selected item
   //in the combo box
   protected function btnCapture_clickHandler(event:MouseEvent):void
   {
    updateResult();
    this.currentState = "result";
   }
   
   //Function that create the ByteArray based on the selected item
   //in the combobox
   private function updateResult():void
   {
    resultArray = null;
    var imageSnap:ImageSnapshot = ImageSnapshot.captureImage(
     cmbViewName.selectedItem.data);
    resultArray = imageSnap.data as ByteArray;
   }
   
   //Upon creationComplete, let us populate the views with data
   protected function creationCompleteHandler(event:FlexEvent):void
   {
    var tempObj:Object;
    for(var i:int = 0; i < 8; i ++)
    {
     tempObj = new Object();
     for(var j:int = 0; j < 3; j ++)
     {
      tempObj["col" + (j+1)] = "Row " + (i + 1) 
       + " Col " + (j + 1); 
     }
     arr.push(tempObj);
    }
    dataGrid.dataProvider = arr;
    
    for(var i = 0; i < 20; i ++)
    {
     tempObj = new Object();
     for(var j = 0; j < 5; j ++)
     {
      tempObj["col" + (j+1)] = "Row " + (i + 1) 
       + " Col " + (j + 1); 
     }
     arr1.push(tempObj);
    }
    dataGrid1.dataProvider = arr1;
    
    this.validateNow();
    tempObj = new Object();
    tempObj.label = "Main Application";
    tempObj.data = viewAll;
    arr_Content.push(tempObj);
    tempObj = new Object();
    tempObj.label = "Top Panel";
    tempObj.data = viewBtnsPanel;
    arr_Content.push(tempObj);
    tempObj = new Object();
    tempObj.label = "Contents";
    tempObj.data = viewContent;
    arr_Content.push(tempObj);
    tempObj = new Object();
    tempObj.label = "1st Data Grid";
    tempObj.data = dataGrid;
    arr_Content.push(tempObj);
    tempObj = new Object();
    tempObj.label = "2nd Data Grid";
    tempObj.data = dataGrid1;
    arr_Content.push(tempObj);
    
    cmbViewName.dataProvider = arr_Content;
    this.validateNow();
    updateResult();
   }
   
   //Upon moving into the 'result' state, update the source 
   //of the image
   protected function state1_enterStateHandler(event:FlexEvent):void
   {
    // TODO Auto-generated method stub
    imgResult.source = resultArray;
   }
   
  ]]>
 </mx:Script> 
 <mx:states>
  <mx:State name="result" enterState="state1_enterStateHandler(event)">
   <mx:RemoveChild target="{cmbViewName}"/>
   <mx:RemoveChild target="{btnCapture}"/>
   <mx:RemoveChild target="{dataGrid}"/>
   <mx:RemoveChild target="{dataGrid1}"/>
   <mx:AddChild position="lastChild" relativeTo="{viewContent}">
    <mx:Image id="imgResult" width="100%" height="100%"
        scaleContent="false"/>
   </mx:AddChild>
  </mx:State>
 </mx:states>
 <mx:VBox width="100%" height="100%" verticalGap="10"
    left="10" right="10" top="10" bottom="10"
    id="viewAll">
  <mx:HBox width="100%" id="viewBtnsPanel">
   <mx:Button id="btnMain" label="Main" 
        click="btnMain_clickHandler(event)"/>
   <mx:Button id="btnResult" label="Result" 
        click="btnResult_clickHandler(event)"/>
   <mx:Spacer width="100%"/>
   <mx:ComboBox id="cmbViewName"/>
   <mx:Button id="btnCapture" label="Capture Now!" 
        click="btnCapture_clickHandler(event)"/>
  </mx:HBox>
  <mx:VBox id="viewContent" width="100%" height="100%" 
     verticalGap="10">
   <mx:DataGrid id="dataGrid" width="100%"/>
   <mx:DataGrid id="dataGrid1" width="100%" height="100%"/>
  </mx:VBox>
 </mx:VBox>
</mx:Application>

* Click here to view the demo of this example.
^ Click here for the source files of this demo.