Viewing Word Documents with Flex

Recently I have started a very interesting project and part of the project is to be able to display .pdf and .doc files in a flex application.

[Source: www.sudafrica.it/documenti%20calcio%202010/Zakumi.doc ]

[Source: http://www.primussoccer.com/Zakumi.pdf]

For the pdf I had luck finding this great tool FlexPaper. The idea behind it was to convert the pdf to swf using PDF2SWF from SWFTools so for every page of the PDF document you had a frame in the swf.
FlexPaper does a great job for viewing .pdf documents, but what about .doc?
Well I guess the solution had to be pretty much the same – convert the doc to swf and display it with FlexPaper. Unfortunately I could not find any good doc to swf converter.

So I asked myself why not just convert the Word document to Adobe Reader document and then do the same what I am doing for .pdf documents (convert them to .swf and view them with FlexPaper). That seemed like the straight-forward solution. Converting .doc to .pdf (and other various formats) is really easy with OpenOffice and it comes for Windows, Linux and Mac OS. When you open your document you just have to click File and Export as PDF.

After that we start PDF2SWF from the command-line with the corresponding options

However if you want to make this process programmatically in a headless mode here is the solution. There is this Java library called JODConverter that relies on OpenOffice that comes here for the help.
So here is how to convert Word documents to Adobe Reader documents programmatically with Java

public static File convertToPDF(File file){
String oldFilePath = file.getAbsolutePath();
//change the extension to .pdf
String newFilePath = oldFilePath.substring(0, oldFilePath.lastIndexOf(".")) + ".pdf";

OfficeManager officeManager = new DefaultOfficeManagerConfiguration().buildOfficeManager();
officeManager.start();

OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager);
File newFile = new File(newFilePath);
converter.convert(file, newFile);

officeManager.stop();

return newFile;
}

After we are done with this, we have to convert the pdf to swf

private static File convertToSWF(File file) throws IOException{
String oldFilePath = file.getAbsolutePath();
//change the extension to .swf
String newFilePath = oldFilePath.substring(0, oldFilePath.lastIndexOf(".")) + ".swf";

//calling pdf2swf to convert our file.pdf to file.swf

String[] command = { "pdf2swf", file.getAbsolutePath(), "-o", newFilePath, "-f",
"-T 10", "-t", "-G"};

Process p = Runtime.getRuntime().exec(command);

BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
log.info(line);
}

return new File(newFilePath);
}

This is my workaround for viewing Word Documents with Flex, maybe you have a better solution? I’d love to hear it.

Advertisements

Custom Flex layout. Safari-like Top Sites

Maybe you are asking yourself “Why am I here?” Well the simple answer is here:

So what is this? It’s a custom flex 4 layout that displays elements as in cylinder. The idea for it I got a long time ago when I saw Safari’s Top Sites. Back then I managed to achieve this look and feel with flex using Papervision3D. But I didn’t like the way I made it at all. So year later I have decided to try again. This time with the native 3D support of Flash Player 10 and the new layouting in Flex 4 it was quite intuitive and the most proper solution.

Enough talking lets see the real thing. So this is the Safari Top Sites that inspired me

And this is the result

You can see the Flex Top Sites application here and the source code here.

The logic of implementing a custom 3d layout I got from these posts of Ryan Campbell and Evtim Georgiev

The basic things are

  1. Create a class that extends LayoutBase
  2. On updateDisplayList position and transform the images
  3. Transforming elements(images) to look like they are in cylinder
    The idea here is to move the element in the back (translate over Z) and then rotate over the Y Axis
  4. Do the same for the other elements but rotate them by different angle and/or translate over Y
  5. Bring the images more in front (because the current look would be if we are in the center of cylinder)
The code for the last 3 is

var matrix:Matrix3D = new Matrix3D();
matrix.appendTranslation(-_columnWidth/2, 0, radius);
matrix.appendRotation(startDegree + degreesMove*j, Vector3D.Y_AXIS);
matrix.appendTranslation(width/2, (_rowHeight + _horizontalGap) * i ,-cameraPosition);
element.setLayoutMatrix3D(matrix, false);

Important thing here is

var pp:PerspectiveProjection = new PerspectiveProjection();
pp.projectionCenter = new Point(width/2, height/2);

element["transform"].perspectiveProjection = pp;

In short words when providing this when we rotate the element the point of the rotation will be the center of the screen (container).

The other things are just plain geometry calculations, like finding the radius, chord, arc of a circle. Really important thing is to imagine both 3D cylinder, 2D circle from top and 2D rectangle from front.

The TopSiteLayout.as provides also options to specify number of rows, columns, width of columns, height of columns, vertical and horizontal gap, although it works just fine if you let it calculate these values itself

And to finish with, I have added this reflection for the 4 images at the bottom. I am using WetFloorFilter from Gonta. It is really good and it is for Flex 4

var filter:WetFloorFilter = new WetFloorFilter();
filter.height = 100;
filter.alpha = 0.5;

BindingUtils.bindProperty(filter, "y", thumbnail, "height");

thumbnail.filters = [filter];

And basically that’s all. You can download the custom layout from here.
Would really like to hear what you think

Browse and download files with Flex, Spring and BlazeDS

After writing about Uploading Files with Flex and Spring via BlazeDS the next logical step is to write another example of how to browse and download them

This example will show how to get a list of all files and folders from a specific directory on the server, display them in the client’s application, browse these directories and/or download specific files.

So you may see that in the previous post I used Flex 4. I really wanted to create this example also with Flex 4, but I had problems with creating an itemRenderer for DataGrid, even found about this MXDataGridItemRenderer but still. I would appreciate if someone would comment about how to get over this. But whatsoever the example that I’m going to show here is created with Flex 3.5 and we all know how cool is it 🙂

This time we will start with the Spring implementation. So first we have this variable

private File currentDirectory = new File(System.getProperty("user.home"));

This will keep track of our current directory, as we will browse the content from the client application. I’ve taken a note from Helen Neely on my previous post and now the path is OS independent and it points to the user’s home directory.

@RemotingInclude
public List listFiles() {
    
    File files[] = currentDirectory.listFiles();
    ArrayList allFiles = new ArrayList();
    
    for(int i = 0; i < files.length; i++){
        if(!files[i].isHidden())
        {
            allFiles.add(new FileInfo(files[i].getName(),
                    files[i].length(), files[i].isDirectory()));
        }
    }
    return allFiles;
}

This method takes all the files and folders that are in the current directory that are not hidden (personal likeness) adds them in an ArrayList and returns the list. You can see that here we are creating new objects of type FileInfo, that is just a small class I wrote that has properties like name, size, isDir and data . There is also a corresponding to this class in the Flex project so we can map them.

@RemotingInclude
public void changeDir(String dir) {
    if(StringUtils.hasText(dir)){
        File tempDirectory = new File(currentDirectory.getPath() + File.separator + dir);
        if(tempDirectory.exists() && tempDirectory.isDirectory())
            currentDirectory = tempDirectory;
    }
}

When called this method with a name of folder we look if there is a folder with this name in our current directory and we change the current directory to it.

@RemotingInclude
public FileInfo downloadFile(String fileName) throws Exception {
    
    File file = new File(currentDirectory.getPath() + File.separator + fileName);
    FileInputStream input = new FileInputStream(file);
    byte data[] = new byte[(int)file.length()];
    input.read(data);
    
    return new FileInfo(file.getName(), file.length(), file.isDirectory(),
            data);
}

Basically this creates a new FileInputStream for the file specified by the current directory and filename and reads it’s data in a array of bytes and then we return a new FileInfo instance that holds these bytes.

Having these methods we are ready to write our client application.
On creation complete we call

remoteBrowseDownload.listFiles();

place a DataGrid and for it’s dataProvider bind to the last result of this operation

<mx:DataGrid id="dataGrid" width="100%" height="100%"
     doubleClickEnabled="true" doubleClick="dataGridDoubleClick(event)"
     dragEnabled="true"
     dataProvider="{remoteBrowseDownload.listFiles.lastResult}">

In the data grid when a user double clicks on a folder we will have to open the folder and display it’s content

public function changeDir(dir:String):void
{
    remoteBrowseDownload.changeDir(dir);
    remoteBrowseDownload.listFiles();
}

Where “dir” is the name of the selected folder.
When the item is not a directory, we have to download it. It is done by this function:

public function downloadFile(file:FileInfo):void
{
    for (var i:int = 0; i < downloadedFiles.length; i++)
    {
        if(file.name == downloadedFiles[i].name && 
            file.size == downloadedFiles[i].size)
            return;
    }
    
    remoteBrowseDownload.downloadFile(file.name);
    
    file.isDownloaded = false;
    downloadedFiles.addItem(file);
}

We are doing a simple check whether this file is already downloaded and if it’s not we call the downloadFile method from our service with only the name of the file and add it in this downloadedFiles array that holds all of our downloaded files. You can see how fast the data is transfered, this is thanks to BlazeDS and AMF.

As this downloading could take longer (depending on the file size), we provide an event handler for the result., that just set the flag isDownloaded to true.

private function onFileDownloaded(event:ResultEvent):void
{
    var file:FileInfo = (FileInfo)(event.result);
    file.isDownloaded = true; 
    for (var i:int = 0; i < downloadedFiles.length; i++)
    {
        if(file.name == downloadedFiles[i].name && 
            file.size == downloadedFiles[i].size)
        {
            downloadedFiles.setItemAt(file, i);
        }
    }                
}

When files are being downloaded we display them in another grid with a progress bar and when the download completes, a button  “save” appears to allow the user to save the file on his file system. To save a file we use the FileReference.save.

public function saveFile(file:FileInfo):void
{
    var fileRef:FileReference = new FileReference();
    fileRef.save(file.data, file.name);
}

Have in mind that use this feature you have to target your application to use Flash player 10.

So basically, that’s it. I’ve also played to add drag and drop support and other stuff, but yo could see by downloading the source.

Download Flex and Spring projects

EDIT: From the Spring project I have made a Maven 2 project, that you can also download

File upload with Flex and Spring

Last I wrote how to connect Flex with Spring. Now I have decided to create an example of uploading multiple files via BlazeDS and here I will show it to you.

You can see the new article on browse and download with Flex, Spring and BlazeDS

So in the example we have 2 buttons (browse and upload) and a data grid  that will display the selected files.



    
        
    

When the first button is clicked we call the browseFiles function that creates an instance of FileReferenceList and opens up the file dialog for browsing.

private function browseFiles(event:MouseEvent):void
{
    fileRefList = new FileReferenceList();
    fileRefList.addEventListener(Event.SELECT, selectFiles);
    fileRefList.browse();
}

Now when the user selected some files and presses “open”, the function selectFiles is called

private function selectFiles(e:Event):void
{                
    for each(var fileRef : FileReference in fileRefList.fileList)
    {
        fileRef.load();
        selectedFiles.push(fileRef.name);
        selectedFileReferences.push(fileRef);
    }
    dataGrid.dataProvider = selectedFiles;
}

It iterates over the list of FileReference (the selected files) and for each it loads the file, adds its name to an array of strings, that we use to show in the data grid and to an array that holds the selected file references, so that we can add more on another pressing of “browse”

Now we should be able to see our data grid filled with names of files.
The next thing to do is to create a function for handling the click of the “Upload Files”  button.

private function uploadFiles(e:MouseEvent):void
{                
    for each(var fileRef : FileReference in selectedFileReferences)
    {
        remoteObjectUpload.doUpload(fileRef.name, fileRef.data);
    }
}

For each FileRefernce we call our remote method by passing the file’s name and the file’s data represented by BytesArray.
The actual Java class and method looks like:

@Service("fileUploadService")
@RemotingDestination(channels = {"my-amf"})
public class FileUploadService {

    //change this to the desired path you wish the files to be uploaded
    private static String File_Path = "C:\\workspace\\Temp\\";
    @RemotingInclude
    public static Boolean doUpload(String fileName, byte[] data) {
        try{
            //create the dir that we will store files
            File dir = new File(File_Path);
            dir.mkdirs();
            
            File file = new File(File_Path + fileName);
            FileOutputStream output = new FileOutputStream(file);
            output.write(data);
            output.close();
        }
        catch(FileNotFoundException e){
            return false;
        }
        catch(IOException e){
            return false;
        }
    
        return true;
    }
}

The doUpload method just creates the directories to the File_Path, creates a new output stream for the given file and writes the bytes.

And aggain you can see the other scenario – downloading the files

You can download the files for both Spring and Flex projects from here:
FileUpload.zip

EDIT: From the Spring project I have made a Maven 2 project, so that you can also download

Flex Spring BlazeDS Integration (on public web server)

This won’t be a post about how to integrate Flex and Spring. Actually I intended to be one, but several days ago James Ward posted this tutorial on how easy it is to do with Flash Builder 4 so I thought it won’t be necessary.

Edit:  you can checkout the new post of how to upload files with Flex and Spring

What I will show is how to upload our project to a free Java and Tomcat host to test it, connect our Flex application to it and show this example here. I’ve been looking for such free hosting and the only thing I’ve found (if there are any others please comment) is eatj.com that provides a free trial limited up to 50 MBs of storage and every 6 hours the server is restarted, but for testing is fine.

I assume that you already created your Spring project based on the tutorial. I’ve added small changes for the current example, if you want the source of the example get it from here. Now right click on the project -> Export -> WAR file and select where to export it.
Now we log into our account in eatj.com and upload the war file, restart the server and this is it.

Now for the Flex part, although I haven’t used the Data Wizards from Flash Builder (as I’ve had small problems) I did it the old fashioned way and the source is:

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
         xmlns:s="library://ns.adobe.com/flex/spark" 
         xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="1024" minHeight="768"
         applicationComplete="remoteObject.addMessage(null, null, null)">
  
    <![CDATA[
      import mx.controls.Alert;
    ]]>
  
  
    
      
        
          
        
      
    
  
  
  
    
      
      
      
    
  
  
    
    
      
    
    
      
    
    
  


Yep that is Flex 4, my first Flex 4 app in my blog 🙂

So now we compile it and we have our swf. For this example I will upload it to http://sites.google.com/ which I use to publish my files to.
There is one last thing to do, to create and configure a crossdomain.xml on our server.

We create on out local machine crossdomain.xml looking like:

<!DOCTYPE cross-domain-policy 
  SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">

   

The weakness here is that everyone from *.googlegroups.com can connect, but as this is an example it is not a problem, and I don’t think someone will be that interested 🙂

Now with your favorite ftp client, connect it to host: s215.eatj.com port:21 your account’s username and password. Navigate to /webapps/ROOT and copy the crossdomain.xml there.

Click here to see the result. EDIT: Unfortunately the eatj trial account shuts down the server 6 hours after it was started. That’s why sometimes you wont be able to connect to the server

Source code:
spring-flex-server
FlexSpring