'00.Flex,Flash,ActionScript'에 해당되는 글 131건

  1. 2013.09.11 How do you set focus in Flash ActionScript 3 (AS3)?
  2. 2013.09.11 Working with States in Flex 4
  3. 2013.09.10 [펌] BlazeDS 와 스프링(Spring) 연동하기 1
  4. 2013.09.09 [펌] (Flex 4)Blazeds + Spring 설정 완료된 war 1
  5. 2013.09.05 spring blazeds integration
  6. 2013.09.04 <flex-remoting> 태그 사용시 발생하는 오류
  7. 2013.09.02 Spring BlazeDS Integration 환경 설정
  8. 2013.09.02 [펌] Spring BlazeDS Integration 1.0.0 Release(정식) 적용하기(1) - 세팅 및 Remoting적용
  9. 2013.09.02 Spring BlazeDS Integration
  10. 2013.08.13 flex style explorer
  11. 2013.07.26 [펌] FLEX에 움직이는 GIF 넣기
  12. 2013.07.26 flex Tree Icon Spinner (not using animated gif)
  13. 2013.07.25 Datagrid avec ItemRenderer avec CheckBox, ComboBox, ColorPicker
  14. 2013.07.24 [펌] Progressbar in Datagrid Example
  15. 2013.07.22 TabNavigator와 ModuleLoader 사용시 문제
  16. 2013.07.19 Copy Flex UIComponent
  17. 2013.07.19 flex DataGrid Border remove
  18. 2013.07.17 flash.printing pagesize
  19. 2013.07.17 flex Printing
  20. 2013.07.17 flex print preview
  21. 2013.07.15 [펌] Flex Print Class Example
  22. 2013.07.15 Using the FlexPrintJob class
  23. 2013.07.15 Flex printing breakthrough! And more!
  24. 2013.07.15 AlivePDF
  25. 2013.07.12 [펌] SpriteVisualElement (Spark) | Flex Examples
  26. 2013.07.09 Printing with Flex: Chapter 11 - Enterprise Development with Flex
  27. 2013.07.05 Printing Flex Data Grids
  28. 2013.07.05 PrintDataGrid,percentHeight and multipage layouts
  29. 2013.07.05 RichTextEditor control
  30. 2013.07.05 Simple TLF Text Editor released
반응형

How do you set focus in Flash ActionScript 3 (AS3)?

Answer

Use the stage focus method:

stage.focus = object;

Source Code

import flash.text.TextField;
import flash.text.TextFieldType;
import fl.controls.Button;

var exampleTextField:TextField = new TextField();
exampleTextField.width = 100;
exampleTextField.height = 20;
exampleTextField.x = 50;
exampleTextField.y = 20;
exampleTextField.type = TextFieldType.INPUT;
exampleTextField.border = true;
addChild(exampleTextField);

var focusInButton:Button = new Button();
focusInButton.label = "Focus";
focusInButton.width = 80;
focusInButton.height = 20;
focusInButton.x = 10;
focusInButton.y = 70;
addChild(focusInButton);

var focusOutButton:Button = new Button();
focusOutButton.label = "Focus Out";
focusOutButton.width = 80;
focusOutButton.height = 20;
focusOutButton.x = 110;
focusOutButton.y = 70;
addChild(focusOutButton);

focusInButton.addEventListener(MouseEvent.MOUSE_UP, function(){ 
  stage.focus = exampleTextField;
});
focusOutButton.addEventListener(MouseEvent.MOUSE_UP, function(){ 
  stage.focus = null;
});
Posted by 1010
반응형

Working with States in Flex 4
by Frank Sommers
September 16, 2009

Advertisements

Summary
Flex 4's new syntax simplifies working with application states. This article provides a tutorial introduction into UI state management with Flex 4, and includes a complete example.

Stateless Web applications aim to store as little state on the server as possible. Reducing, or even eliminating, reliance on server-side state makes it easier to implement a scalable infrastructure, at least in principle.

At the same time, only the simplest Web applications can function completely without state. Dispatching a request to an online weather reporting service may work in a purely stateless fashion, but more sophisticated applications must keep track of where a user is in a complex data entry process, possible data validation errors, and so on. Data-intensive applications may also need to cache a user's frequently accessed data in order to reduce response time.

Client-Side State

Instead of storing state data on the server, rich-client applications may choose to store such information on the client. A key benefit of rich-client frameworks is that they provide APIs and programming language constructs that make it easier to work with application state on the client. In addition to client-side collections APIs and language support for data processing, rich-client frameworks typically offer high-level constructs to manage application state related to display logic as well.

Consider, for example, a sign-in component typical of many Web applications: A user is presented with text input boxes to enter a user name and a password, as well as a button to initiate the sign-in. A login box may also provide a handy way for the user to create a new account. If the user chooses to register a new account, another view is presented with text boxes for additional information, such as first and last names, and so forth:

Click on "Need to register?" to toggle between UI states. To view the source code, right-click or control-click on the application.

In a traditional, server-generated HTML user interface, the server may return an entirely newly rendered page when a user chooses to register instead of signing in. By contrast, a rich-client can affect the change in the user interface entirely on the client side, without interaction from the server.

The simplest way to code that is to write display logic that inserts and removes new labels and text boxes in the UI at runtime, for instance, by modifying the UI's DOM in the browser. More sophisticated rich-client frameworks, however, provide a much handier abstraction to accomplish the same goal: UI states.

UI States in Flex

A UI state is the set of all components and component properties in effect in a component at any given point in time. Multiple UI states can be associated with a component: Switching from one state to another causes the rich-client runtime to add, remove, or modify components based on the differences between states. The login form, for instance, could have "login" and "register" states: The register state defines additional input boxes, changes the label of the button component from "Sign in" to "Register", and also causes the button click to invoke a register() method instead of signin().

In Flex, defining and working with application states has been possible since the 2.0 version of the Flex SDK. In Flex versions 2 and 3, states were defined inside a states array in an MXML document; inside each state definition, in turn, were declarations of what should be added, removed, or modified, related to some base state. It was also possible to build one state on another.

 

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"> 

  <mx:Script>
      <![CDATA[
            private function signin():void {
                // Implementation of signin
            }

            private function register():void {
                // Implementation of register
            }
        ]]>
  </mx:Script>

  <mx:states>

    <mx:State name="Register">        

        <mx:AddChild relativeTo="{loginForm}" position="lastChild" creationPolicy="all">
            <mx:FormItem id="frmPasswordConfirm" label="Confirm:">
                <mx:TextInput id="passwordConfirm" displayAsPassword="true"/>
            </mx:FormItem>        
        </mx:AddChild>

        <mx:AddChild relativeTo="{loginForm}" position="lastChild">
            <mx:FormItem label="First name:" id="frmFirstName">
                <mx:TextInput id="firstName" width="220"/>
            </mx:FormItem>
        </mx:AddChild>

        <mx:AddChild relativeTo="{loginForm}" position="lastChild">            
            <mx:FormItem label="Last name:" id="frmLastName">
                <mx:TextInput id="lastName" width="220"/>
            </mx:FormItem>
        </mx:AddChild>

        <mx:AddChild relativeTo="{loginButton}" position="after">
            <mx:LinkButton label="Return to login" click="setCurrentUIState()"/>
        </mx:AddChild>            

        <mx:RemoveChild target="{registerLink}"/>            

        <mx:SetProperty target="{loginLabel}" name="text" value="Create a new account:"/>    
        <mx:SetProperty target="{loginButton}" name="enabled" value="false"/>
        <mx:SetProperty target="{loginButton}" name="label" value="Register"/>
            
     </mx:State>        

  </mx:states>
    
 <mx:Label text="Sign In:" 
            fontWeight="bold" paddingLeft="12" fontSize="14" 
            id="loginLabel" width="100%"/>
        
        <mx:Form id="loginForm" width="100%">

            <mx:FormItem label="Email:" id="frmEmail">
                <mx:TextInput id="email" width="220"/>
            </mx:FormItem>            

            <mx:FormItem label="Password:" id="frmPassword">
                <mx:TextInput id="password" displayAsPassword="true" width="220"/>
            </mx:FormItem>        
        </mx:Form>

        <mx:CheckBox id="rememberMe" label="Remember me on this computer"    
            paddingLeft="77" selected="true"/>

        <mx:ControlBar width="100%"    paddingLeft="84">
            <mx:Button label="Sign in" id="loginButton" click="signin()" enabled="false"/>            
            <mx:LinkButton label="Need to Register?" id="registerLink"
                click="currentState='Register'"/>            
        </mx:ControlBar>
</mx:VBox>

Listing 1: Login box with Flex 3

In the above Flex 3.0 implementation, AddChild and RemoveChild tags indicate that the runtime should add or remove child components, respectively, in the register state. The SetProperty tag, in turn, changes the button's label to "Register," and the SetEventHandler tag ensures that a register() method is invoked when the UI is the register state. Clicking on the LinkButton component toggles between the signin and register states.

While the Flex 2 and 3 state syntax works well, adding and removing components is rather cumbersome: For instance, you have to specify where new components are to be added apart from the new components' parents. UI components supporting a large number of states end up becoming unwieldy, with significant portions of related UI logic scattered across a large MXML file.

Flex 4's State Improvements

To simplify state definition, Flex 4 introduces a new states syntax. Although the various component states are still declared inside the states tag, the definition of each state is specified inline. To see how that works, consider the Flex 4 implementation of the login component. To make the Flex 4 implementation mirror the Flex 3 version as closely as possible, we didn't define separate component and skin files, a Flex 4 best practice:

<?xml version="1.0" encoding="utf-8"?>
<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">
	
    <fx:Script>
        <![CDATA[
            private function signin(): void {
                 // Implementation of signin 
            }

            private function register(): void {
                // Implementation of register
            }
        ]]>
    </fx:Script>
	
    <s:states>
        <s:State name="signin"/>
        <s:State name="register"/>
    </s:states>
	
    <s:Group id="mainGroup">
        <s:layout>
            <s:VerticalLayout/>
        </s:layout>
		
    <mx:Label text="Sign in:" fontWeight="bold" paddingLeft="12" fontSize="14" id="loginLabel" width="100%"/>
		
    <mx:Form id="loginForm" width="100%" paddingTop="3" paddingBottom="3">

        <mx:FormItem label="First name:" includeIn="register" id="firstNameItem" alpha="0.0">
            <s:TextInput id="firstName" width="220"/>
        </mx:FormItem>

        <mx:FormItem label="Last name:" includeIn="register" id="lastNameItem" alpha="0.0">
            <s:TextInput id="lastName" width="220"/>
        </mx:FormItem>

        <mx:FormItem label="Email:" id="emailItem">
            <s:TextInput id="email" width="220"/>			
        </mx:FormItem>

        <mx:FormItem label="Password:" id="passwordItem">
            <s:TextInput id="password" displayAsPassword="true" width="220"/>
        </mx:FormItem>

        <mx:FormItem label="Confirm:" includeIn="register" id="confirmItem" alpha="0.0">
             <s:TextInput id="passwordConfirmation" displayAsPassword="true" width="220"/>			
        </mx:FormItem>
    </mx:Form>

    <s:Group>
        <s:layout>
            <s:HorizontalLayout paddingLeft="100"/>
        </s:layout>
        <s:Button label="Sign in" label.register="Register" id="loginButton" 
            enabled="true" click.signin="signin()" click.register="register()"/>
			
        <mx:LinkButton label="Need to register?" 
            click="currentState = 'register'" includeIn="signin"/>
        <mx:LinkButton label="Return to signin"
            click="currentState = 'signin'" includeIn="register"/>
     </s:Group>
		
    <mx:CheckBox id="rememberMe" label="Remember me on this computer" paddingLeft="110"/>
 </s:Group>
</s:Application>

Listing 2: Login box with Flex 4 state syntax

Instead of AddChild and RemoveChild methods, components that should be visible in the register state only are added an includeIn="register" tag. You can specify a comma-separated list of state names for the includeIn tag. Flex 4 also provides an excludeFrom tag, if you need to declare what states a component should be removed from. This new syntax allows you to declare components in place, inside the parent.

Properties can similarly be set inline using a new dot-separated syntax. For example, the Button component's label property is defined as follows:

... label="Sign in" label.register="Register" 

This tells Flex to set the button's label property to "Register" in the register state, and to "Sign in" in other states (the default). And listeners can be declared similarly: When the component is in the register state, the register() method is invoked on a button click, otherwise the signin() method is called:

click="signin()" click.register="register()"

The new component syntax makes it much easier to define sophisticated components with relatively straightforward, declarative code. The Flex 4 component states syntax also supports state groups as well as the ability to assign a new parent to a component. It is important to note that the new syntax only impacts MXML declarations—which is what the Flex compiler uses to generate ActionScript code. States can be defined in ActionScript as well, but that syntax does not change in Flex 4.

Flex gives you convenient ways not only to define component states, but also to specify transitions between states. To make the transition between the signin and register states smoother, this example includes a transition effect between those states. A forthcoming Artima article will describe Flex 4 transitions in greater detail.

Share Your Opinion

Have an opinion on Flex 4's states syntax? Discuss this article in the Articles Forum topic, Working with States in Flex 4.

Resources

Adobe's Flash Builder 4
http://labs.adobe.com/technologies/flashbuilder4

Flex 4 SDK
http://opensource.adobe.com/wiki/display/flexsdk/Flex+SDK

Gumbo Project
http://opensource.adobe.com/wiki/display/flexsdk/Gumbo

Flex.org
http://www.flex.org

About the Authors

Frank Sommers is Editor-in-Chief of Artima.

Posted by 1010
반응형

 

 

BlazeDS 와 스프링(Spring) 연동하기

 

출처 : http://youthgonewild.co.kr/category/FLEX

 

blazeds-spring-beta1.jar



BlazeDS를 이용하여 Remote Object Service를 이용할 때 스프링과 연동할 수는 없을까 라는 의문을 가지고
자료를 찾아보았습니다. 역시 이미 연동해서 사용하는 방법이 있더군요

다음의 remoting-config.xml 파일을 봅시다.

 <!-- 스프링 프레임워크를 이용한 remote object -->
<destination id="simpleLoadService">
 <properties>
  <factory>springfactory</factory> 
  <source>simpleLoadDao</source> 
 </properties>
</destination>    

일반적인 destination을 정의하는 부분과 다릅니다. 바로 <factory>라는 부분인데요, springfactory 라는
새로운 factory를 정의해서 스프링과 remote object를 연결시실수 있습니다.
그러면 springfactory 가 정의된 부분이 있어야 겠죠?

바로 services-config.xml 파일에 정의합니다.

 <!-- 스프링 프레임워크를 사용할수 있게 하기 위한 팩토리 설정  -->
 <factories>
  <factory id="springfactory" class="flex.messaging.factory.SpringFactory" /> 
 </factories>


위의 내용이 springfactory를 정의한 부분입니다.
이제 이 factory를 통해서 스프링과 remote object가 연결됩니다.
그러면 flex.messaging.factory.SpringFactory 는 원래 flex package에 존재하는 클래스일까요?
그렇지 않습니다. 스프링과의 연동을 위해 따로 제작된 클래스죠. 저도 외국 사이트에서 다운로드 했습니다.
첨부파일로 올리니 다운 받아서 lib에 등록해서 사용하시면 됩니다..


remoting-config.xml 에서 source로 설정되어 있는 simpleLoadDao도 마저 살펴봅시다.
스프링의 bean설정파일인 applicationContext.xml 파일에 등록되어 있겠죠.

applicationContext.xml

 <bean id="simpleLoadDao" class="flexintegration.spring.sample.SimpleLoadDao">
   <property name="sqlMapClient" ref="sqlMapClient" />
  </bean>

 <!-- SqlMap setup for iBATIS Database Layer : 스프링 프레임워크와 iBATIS의 연동  -->
 <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
  <property name="configLocation" value="WEB-INF/ibatis/sql-map-config.xml" />
  <property name="dataSource" ref="dataSource" />
 </bean>

  <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="${jdbc.driverClassName}"/>
      <property name="url" value="${jdbc.url}"/>
      <property name="username" value="${jdbc.username}"/>
      <property name="password" value="${jdbc.password}"/>
  </bean>

  <bean id="propertyConfigurer" 
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="locations">
          <list>
              <value>classpath:jdbc.properties</value>
          </list>
      </property>
  </bean>


이렇게 설정이 되면 Remote Object로 바로 스프링의 DI(Dependency Injection)를 이용하는 bean을 
사용할수가 있게 됩니다. ( iBatis를 이용하고 있어서 iBatis에 대한 경험이 없으시면 약간 이해하기
힘들수도 있겠네요 )

여기까지 Remote Object로 스프링 bean을 연동해서 사용하는 방법에 대해서 알아보았습니다.
Remote Object로 스프링의 강력한 DI를 바로 사용할 수 있다는 건 대단한 장점이죠.

참고로 AOP도 Remote Object 사용시 제대로 동작합니다. ( 테스트 해 보았죠 )

스프링과의 연동을 생각하셨던 분들에게 도움이 되셨기를...

 

Posted by 1010
반응형

출처 : http://softworld.tistory.com/entry/Flex-4Blazeds-Spring-%EC%84%A4%EC%A0%95-%EC%99%84%EB%A3%8C%EB%90%9C-war

 

blazeds-spring.war

 

blazeds.war

 

Adobe가 제공 다운로드에다가 장난질을 쳐놨넹..

Turnkey안에 spring 설정된 war가 있었는데 지워놓은듯... spring설정시 의존성때문에 잘 되지도 않더만 이걸 지워 놓으면 고생할 사람많겠구만...

LiveCycle Data Services살릴려고 Blazeds는 개발 완전 중단되있고 그나마 남은 Freamwork는 graniteds

우선 제공되는 blazeds + spring 연동 war file을 남겨 본다. Test 결과는 잘 돌아감!!!

blazeds-spring.war

Blazeds Ver.4 + Spring Ver.3.0.1

spring ver.을 최신으로 올려봤지만 의존성때문에 잘 안됨..

blazeds.war

기본 Blazeds ver.4 도 올려 놓는다!

Posted by 1010
반응형

출처 : http://lahuman.tistory.com/55

 

주요 몇가지 소스를 간단히 소개 하면,

1. /WEB-INF/web.xml


 <!-- The front controller of this Spring Web application, responsible for handling all application requests -->
 <servlet>
     <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>/WEB-INF/config/web-application-config.xml</param-value>
     </init-param>
     <load-on-startup>1</load-on-startup>
 </servlet>

 <!-- Map all *.spring requests to the DispatcherServlet for handling -->
    <servlet-mapping>
        <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
        <url-pattern>/spring/*</url-pattern>
    </servlet-mapping>


2. /WEB-INF/flex/services-config.xml

        <channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
            <endpoint url="http://{server.name}:{server.port}/{context.root}/spring/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>


3. application-context.xml

 <!-- Maps request paths at /* to the BlazeDS MessageBroker -->
 <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
     <property name="mappings">
         <value>
             /*=mySpringManagedMessageBroker
         </value>
     </property>
 </bean>
 
 <!-- Dispatches requests mapped to a MessageBroker -->
 <bean class="org.springframework.flex.messaging.servlet.MessageBrokerHandlerAdapter"/>
 <!-- Bootstraps and exposes the BlazeDS MessageBroker -->
 <bean id="mySpringManagedMessageBroker" class="org.springframework.flex.messaging.MessageBrokerFactoryBean" /> 
    <!-- Implementation of ProductDAO using low-level JDBC -->
    <bean id="productDAO" class="flex.spring.samples.product.ProductDAO" >
        <constructor-arg ref="dataSource"/>
    </bean>
   
 <!-- Expose the productDAO bean for BlazeDS remoting -->
 <bean id="product" class="org.springframework.flex.messaging.remoting.FlexRemotingServiceExporter">
     <property name="messageBroker" ref="mySpringManagedMessageBroker"/>
     <property name="service" ref="productDAO"/>
 </bean>
   



위 예제를 보면 Spring MVC를 이용해서 flex BlazeDS를 호출 한다.

아래 파일을 찬찬히 봐라!

Posted by 1010
반응형

시스템 환경

BlazeDS 4.0.x

Spring 3.0.x

SBI 1.3 or 1.5

 

Webapplicationcontext.xml에서 <flex-remoting> 태그 사용시 발생하는 오류

예)

    <bean id="SimpleExampleService2" class="com.mydomain.data.SimpleExample">

        <flex:remoting-destination/>

    </bean>

 

오류메시지

SEVERE: Context initialization failed

org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [org.springframework.flex.core.MessageInterceptionAdvice] for bean with name 'org.springframework.flex.core.MessageInterceptionAdvice#0' defined in null: problem with class file or dependent class; nested exception is java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor

    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1211)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:568)

    at org.springframework.beans.factory.support.AbstractBeanFactory.getType(AbstractBeanFactory.java:523)

    at org.springframework.flex.config.RemotingAnnotationPostProcessor.findRemotingDestinations(RemotingAnnotationPostProcessor.java:148)

    at org.springframework.flex.config.RemotingAnnotationPostProcessor.postProcessBeanFactory(RemotingAnnotationPostProcessor.java:79)

    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:624)

    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:614)

    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:398)

    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:443)

    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:459)

    at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:340)

    at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:307)

    at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:127)

    at javax.servlet.GenericServlet.init(GenericServlet.java:211)

    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1164)

    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:984)

    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4043)

    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4349)

    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1013)

    at org.apache.catalina.core.StandardHost.start(StandardHost.java:718)

    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1013)

    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:442)

    at org.apache.catalina.core.StandardService.start(StandardService.java:450)

    at org.apache.catalina.core.StandardServer.start(StandardServer.java:709)

    at org.apache.catalina.startup.Catalina.start(Catalina.java:551)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

    at java.lang.reflect.Method.invoke(Method.java:597)

    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:287)

    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:412)

Caused by: java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor

    at java.lang.ClassLoader.defineClass1(Native Method)

    at java.lang.ClassLoader.defineClass(ClassLoader.java:620)

    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)

    at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:1814)

    at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:868)

    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1321)

    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1200)

    at org.springframework.util.ClassUtils.forName(ClassUtils.java:258)

    at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:408)

    at org.springframework.beans.factory.support.AbstractBeanFactory.doResolveBeanClass(AbstractBeanFactory.java:1229)

    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1200)

    ... 30 more

Feb 20, 2012 5:19:32 PM org.apache.catalina.core.ApplicationContext log

SEVERE: StandardWrapper.Throwable

org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [org.springframework.flex.core.MessageInterceptionAdvice] for bean with name 'org.springframework.flex.core.MessageInterceptionAdvice#0' defined in null: problem with class file or dependent class; nested exception is java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor

    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1211)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:568)

    at org.springframework.beans.factory.support.AbstractBeanFactory.getType(AbstractBeanFactory.java:523)

    at org.springframework.flex.config.RemotingAnnotationPostProcessor.findRemotingDestinations(RemotingAnnotationPostProcessor.java:148)

    at org.springframework.flex.config.RemotingAnnotationPostProcessor.postProcessBeanFactory(RemotingAnnotationPostProcessor.java:79)

    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:624)

    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:614)

    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:398)

    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:443)

    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:459)

    at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:340)

    at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:307)

    at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:127)

    at javax.servlet.GenericServlet.init(GenericServlet.java:211)

    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1164)

    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:984)

    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4043)

    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4349)

    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1013)

    at org.apache.catalina.core.StandardHost.start(StandardHost.java:718)

    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1013)

    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:442)

    at org.apache.catalina.core.StandardService.start(StandardService.java:450)

    at org.apache.catalina.core.StandardServer.start(StandardServer.java:709)

    at org.apache.catalina.startup.Catalina.start(Catalina.java:551)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

    at java.lang.reflect.Method.invoke(Method.java:597)

    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:287)

    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:412)

Caused by: java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor

    at java.lang.ClassLoader.defineClass1(Native Method)

    at java.lang.ClassLoader.defineClass(ClassLoader.java:620)

    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)

    at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:1814)

    at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:868)

    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1321)

    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1200)

    at org.springframework.util.ClassUtils.forName(ClassUtils.java:258)

    at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:408)

    at org.springframework.beans.factory.support.AbstractBeanFactory.doResolveBeanClass(AbstractBeanFactory.java:1229)

    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1200)

    ... 30 more

Feb 20, 2012 5:19:32 PM org.apache.catalina.core.StandardContext loadOnStartup

SEVERE: Servlet /web1 threw load() exception

java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor

    at java.lang.ClassLoader.defineClass1(Native Method)

    at java.lang.ClassLoader.defineClass(ClassLoader.java:620)

    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)

    at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:1814)

    at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:868)

    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1321)

    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1200)

    at org.springframework.util.ClassUtils.forName(ClassUtils.java:258)

    at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:408)

    at org.springframework.beans.factory.support.AbstractBeanFactory.doResolveBeanClass(AbstractBeanFactory.java:1229)

    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1200)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:568)

    at org.springframework.beans.factory.support.AbstractBeanFactory.getType(AbstractBeanFactory.java:523)

    at org.springframework.flex.config.RemotingAnnotationPostProcessor.findRemotingDestinations(RemotingAnnotationPostProcessor.java:148)

    at org.springframework.flex.config.RemotingAnnotationPostProcessor.postProcessBeanFactory(RemotingAnnotationPostProcessor.java:79)

    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:624)

    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:614)

    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:398)

    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:443)

    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:459)

    at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:340)

    at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:307)

    at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:127)

    at javax.servlet.GenericServlet.init(GenericServlet.java:211)

    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1164)

    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:984)

    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4043)

    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4349)

    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1013)

    at org.apache.catalina.core.StandardHost.start(StandardHost.java:718)

    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1013)

    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:442)

    at org.apache.catalina.core.StandardService.start(StandardService.java:450)

    at org.apache.catalina.core.StandardServer.start(StandardServer.java:709)

    at org.apache.catalina.startup.Catalina.start(Catalina.java:551)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

    at java.lang.reflect.Method.invoke(Method.java:597)

    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:287)

    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:412)

 

해결방법

Aopalliance-1.0.jar lib폴더에 추가

 

오류메시지

SEVERE: Context initialization failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_messageBrokerDefaultHandlerMapping': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_messageBroker': Invocation of init method failed; nested exception is org.springframework.beans.factory.BeanInitializationException: MessageBroker initialization failed; nested exception is org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:450)

    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:290)

    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)

    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:287)

    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189)

    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:557)

    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:842)

    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:416)

    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:443)

    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:459)

    at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:340)

    at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:307)

    at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:127)

    at javax.servlet.GenericServlet.init(GenericServlet.java:211)

    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1164)

    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:984)

    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4043)

    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4349)

    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1013)

    at org.apache.catalina.core.StandardHost.start(StandardHost.java:718)

    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1013)

    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:442)

    at org.apache.catalina.core.StandardService.start(StandardService.java:450)

    at org.apache.catalina.core.StandardServer.start(StandardServer.java:709)

    at org.apache.catalina.startup.Catalina.start(Catalina.java:551)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

    at java.lang.reflect.Method.invoke(Method.java:597)

    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:287)

    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:412)

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_messageBroker': Invocation of init method failed; nested exception is org.springframework.beans.factory.BeanInitializationException: MessageBroker initialization failed; nested exception is org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1401)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:512)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:450)

    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:290)

    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)

    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:287)

    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189)

    at org.springframework.beans.factory.support.AbstractBeanFactory.isSingleton(AbstractBeanFactory.java:392)

    at org.springframework.context.support.AbstractApplicationContext.isSingleton(AbstractApplicationContext.java:1024)

    at org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.registerHandler(AbstractUrlHandlerMapping.java:370)

    at org.springframework.web.servlet.handler.SimpleUrlHandlerMapping.registerHandlers(SimpleUrlHandlerMapping.java:129)

    at org.springframework.web.servlet.handler.SimpleUrlHandlerMapping.initApplicationContext(SimpleUrlHandlerMapping.java:104)

    at org.springframework.context.support.ApplicationObjectSupport.initApplicationContext(ApplicationObjectSupport.java:119)

    at org.springframework.web.context.support.WebApplicationObjectSupport.initApplicationContext(WebApplicationObjectSupport.java:71)

    at org.springframework.context.support.ApplicationObjectSupport.setApplicationContext(ApplicationObjectSupport.java:73)

    at org.springframework.context.support.ApplicationContextAwareProcessor.doProcess(ApplicationContextAwareProcessor.java:116)

    at org.springframework.context.support.ApplicationContextAwareProcessor.postProcessBeforeInitialization(ApplicationContextAwareProcessor.java:99)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:394)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1394)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:512)

    ... 31 more

Caused by: org.springframework.beans.factory.BeanInitializationException: MessageBroker initialization failed; nested exception is org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.

    at org.springframework.flex.core.MessageBrokerFactoryBean.afterPropertiesSet(MessageBrokerFactoryBean.java:185)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1460)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1398)

    ... 50 more

Caused by: org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.

    at org.springframework.aop.framework.DefaultAopProxyFactory.createAopProxy(DefaultAopProxyFactory.java:67)

    at org.springframework.aop.framework.ProxyCreatorSupport.createAopProxy(ProxyCreatorSupport.java:104)

    at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:112)

    at org.springframework.flex.core.EndpointConfigProcessor.processAfterStartup(EndpointConfigProcessor.java:69)

    at org.springframework.flex.core.MessageBrokerFactoryBean.processAfterStart(MessageBrokerFactoryBean.java:294)

    at org.springframework.flex.core.MessageBrokerFactoryBean.afterPropertiesSet(MessageBrokerFactoryBean.java:168)

    ... 52 more

Feb 20, 2012 5:21:11 PM org.apache.catalina.core.ApplicationContext log

SEVERE: StandardWrapper.Throwable

org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_messageBrokerDefaultHandlerMapping': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_messageBroker': Invocation of init method failed; nested exception is org.springframework.beans.factory.BeanInitializationException: MessageBroker initialization failed; nested exception is org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:450)

    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:290)

    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)

    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:287)

    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189)

    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:557)

    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:842)

    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:416)

    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:443)

    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:459)

    at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:340)

    at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:307)

    at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:127)

    at javax.servlet.GenericServlet.init(GenericServlet.java:211)

    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1164)

    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:984)

    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4043)

    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4349)

    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1013)

    at org.apache.catalina.core.StandardHost.start(StandardHost.java:718)

    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1013)

    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:442)

    at org.apache.catalina.core.StandardService.start(StandardService.java:450)

    at org.apache.catalina.core.StandardServer.start(StandardServer.java:709)

    at org.apache.catalina.startup.Catalina.start(Catalina.java:551)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

    at java.lang.reflect.Method.invoke(Method.java:597)

    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:287)

    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:412)

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_messageBroker': Invocation of init method failed; nested exception is org.springframework.beans.factory.BeanInitializationException: MessageBroker initialization failed; nested exception is org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1401)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:512)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:450)

    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:290)

    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)

    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:287)

    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189)

    at org.springframework.beans.factory.support.AbstractBeanFactory.isSingleton(AbstractBeanFactory.java:392)

    at org.springframework.context.support.AbstractApplicationContext.isSingleton(AbstractApplicationContext.java:1024)

    at org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.registerHandler(AbstractUrlHandlerMapping.java:370)

    at org.springframework.web.servlet.handler.SimpleUrlHandlerMapping.registerHandlers(SimpleUrlHandlerMapping.java:129)

    at org.springframework.web.servlet.handler.SimpleUrlHandlerMapping.initApplicationContext(SimpleUrlHandlerMapping.java:104)

    at org.springframework.context.support.ApplicationObjectSupport.initApplicationContext(ApplicationObjectSupport.java:119)

    at org.springframework.web.context.support.WebApplicationObjectSupport.initApplicationContext(WebApplicationObjectSupport.java:71)

    at org.springframework.context.support.ApplicationObjectSupport.setApplicationContext(ApplicationObjectSupport.java:73)

    at org.springframework.context.support.ApplicationContextAwareProcessor.doProcess(ApplicationContextAwareProcessor.java:116)

    at org.springframework.context.support.ApplicationContextAwareProcessor.postProcessBeforeInitialization(ApplicationContextAwareProcessor.java:99)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:394)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1394)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:512)

    ... 31 more

Caused by: org.springframework.beans.factory.BeanInitializationException: MessageBroker initialization failed; nested exception is org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.

    at org.springframework.flex.core.MessageBrokerFactoryBean.afterPropertiesSet(MessageBrokerFactoryBean.java:185)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1460)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1398)

    ... 50 more

Caused by: org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.

    at org.springframework.aop.framework.DefaultAopProxyFactory.createAopProxy(DefaultAopProxyFactory.java:67)

    at org.springframework.aop.framework.ProxyCreatorSupport.createAopProxy(ProxyCreatorSupport.java:104)

    at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:112)

    at org.springframework.flex.core.EndpointConfigProcessor.processAfterStartup(EndpointConfigProcessor.java:69)

    at org.springframework.flex.core.MessageBrokerFactoryBean.processAfterStart(MessageBrokerFactoryBean.java:294)

    at org.springframework.flex.core.MessageBrokerFactoryBean.afterPropertiesSet(MessageBrokerFactoryBean.java:168)

    ... 52 more

Feb 20, 2012 5:21:11 PM org.apache.catalina.core.StandardContext loadOnStartup

SEVERE: Servlet /web1 threw load() exception

org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.

    at org.springframework.aop.framework.DefaultAopProxyFactory.createAopProxy(DefaultAopProxyFactory.java:67)

    at org.springframework.aop.framework.ProxyCreatorSupport.createAopProxy(ProxyCreatorSupport.java:104)

    at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:112)

    at org.springframework.flex.core.EndpointConfigProcessor.processAfterStartup(EndpointConfigProcessor.java:69)

    at org.springframework.flex.core.MessageBrokerFactoryBean.processAfterStart(MessageBrokerFactoryBean.java:294)

    at org.springframework.flex.core.MessageBrokerFactoryBean.afterPropertiesSet(MessageBrokerFactoryBean.java:168)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1460)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1398)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:512)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:450)

    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:290)

    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)

    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:287)

    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189)

    at org.springframework.beans.factory.support.AbstractBeanFactory.isSingleton(AbstractBeanFactory.java:392)

    at org.springframework.context.support.AbstractApplicationContext.isSingleton(AbstractApplicationContext.java:1024)

    at org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.registerHandler(AbstractUrlHandlerMapping.java:370)

    at org.springframework.web.servlet.handler.SimpleUrlHandlerMapping.registerHandlers(SimpleUrlHandlerMapping.java:129)

    at org.springframework.web.servlet.handler.SimpleUrlHandlerMapping.initApplicationContext(SimpleUrlHandlerMapping.java:104)

    at org.springframework.context.support.ApplicationObjectSupport.initApplicationContext(ApplicationObjectSupport.java:119)

    at org.springframework.web.context.support.WebApplicationObjectSupport.initApplicationContext(WebApplicationObjectSupport.java:71)

    at org.springframework.context.support.ApplicationObjectSupport.setApplicationContext(ApplicationObjectSupport.java:73)

    at org.springframework.context.support.ApplicationContextAwareProcessor.doProcess(ApplicationContextAwareProcessor.java:116)

    at org.springframework.context.support.ApplicationContextAwareProcessor.postProcessBeforeInitialization(ApplicationContextAwareProcessor.java:99)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:394)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1394)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:512)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:450)

    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:290)

    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)

    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:287)

    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189)

    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:557)

    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:842)

    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:416)

    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:443)

    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:459)

    at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:340)

    at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:307)

    at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:127)

    at javax.servlet.GenericServlet.init(GenericServlet.java:211)

    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1164)

    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:984)

    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4043)

    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4349)

    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1013)

    at org.apache.catalina.core.StandardHost.start(StandardHost.java:718)

    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1013)

    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:442)

    at org.apache.catalina.core.StandardService.start(StandardService.java:450)

    at org.apache.catalina.core.StandardServer.start(StandardServer.java:709)

    at org.apache.catalina.startup.Catalina.start(Catalina.java:551)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

    at java.lang.reflect.Method.invoke(Method.java:597)

    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:287)

    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:412)

 

해결방법

cglib-nodep-2.2.jar 라이브러리 추가

 

참조

http://develop.sunshiny.co.kr/80 (스프링 라이브러리 의존성)

Posted by 1010
반응형

3.Spring BlazeDS Integration 설치

본 문서에서는 Flex Plugin을 사용하지 않고 별도로 Flex개발 환경을 구성하는 방법에 대해서 다루도록 한다. Spring BlazeDS Integration을 설치 하기 위해서는 BlazeDS가 먼저 설치 되어야 한다. Web Application 프로젝트에 BalzeDS를 설치 한 후, Spring BlazeDS Integration을 추가로 설치 하는 방법에 대해 살펴 보도록한다.

3.1.BlazeDS의 설치

Adobe Open Source Site에서 최신 버전의 BlazeDS을 다운 받을 수 있다. Turnkey버전과 Binary Distribution을 다운 받을 수 있다. Turnkey의 경우 BlazeDS설치 파일 이외에 Reference 문서, Sample Application, BlazeDS Console Application등이 포함되어 있다.

BlazeDS를 다운 받아 압축을 풀면 blazeds.war파일이 포함되어 있는다. blazeds.war파일은 아래와 같은 폴더들로 구성된다.

다음은 각 폴더와 파일들에 대한 간략한 설명이다.

  • WEB-INF/web.xml : 어플리케이션의 배포지시자로 MessageBrokerServlet에 대한 Servlet설정과 HttpFlexSession 등에 대해 설정되어 있다. SpringBlazeDS Integration을 사용햘 경우에는 MessageBrokerServlet이 아닌 SpringMVC의 DispatcherServlet이 Client요청을 처리 할 수 있도록 Servlet설정을 변경해야 한다.

  • WEB-INF/flex : BlazeDS의 환경 설정 파일들이 있다. 서버측과 통신하기 위한 채널, 아답터, 로깅등의 정보가 세팅되어 있는 파일들이 위치한다.

  • WEB-INF/lib : BlazeDS 서버 측 라이브러리들이 위치해 있다.

3.1.1.BlazeDS 설정 파일

BlazeDS의 WEB-INF/flex폴더의 4개의 xml파일(services-config.xml, remoting-config.xml, proxy-config.xml, messaging-config.xml)을 Web Application Project의 {Web Root folder}/WEB-INF/flex란 이름의 폴더를 생성한 후 아래 그림과 같이 복사한다.

service-config.xml 파일을 열어 remoting-config.xml, messaging-config.xml파일의 include 부분을 삭제 또는 주석처리 한다. 두 파일을 include에서 제외하는 이유는 Spring BlazeDS Integration의 설정파일에서 Remoting, Messaging 통신에 대한 환경설정을 할 수 있기 때문이다. 그리고 Web Application 레벨의 default-channels을 설정한다. 아래는 수정된 service-config.xml파일의 일부이다.

<services-config>
    <services>
        <!-- service-include file-path="remoting-config.xml" / -->
        <service-include file-path="proxy-config.xml" />
        <!-- service-include file-path="messaging-config.xml" / -->  
        <default-channels>
        	<channel ref="my-amf"/>
    	</default-channels>      
    </services>

    <security>
    <!-- 중략 -->

3.1.2.BlazeDS 라이브러리

BlazeDS의 WEB-INF/lib폴더의 BlazeDS 서버 측 라이브러리를 Web Application Project의 {Web Root folder}/WEB-INF/lib폴더에 복사한다. 이 때 Web Application에 이미 등록되어 있는 라이브러리와 충돌나지 않도록 확인해서 복사한다. 예를들어 Anyframe의 Foundation Plugin으로 설치된 Web Application에는 common-logging-1.1.1.jar을 포함하고 있으므로 common-logging.jar은 제외한 후 복사한다.

3.2.Spring BlazeDS Integration 설치

BlazeDS설치가 끝났으면 Spring BlazeDS Integration을 Web Application에 설치 하도록 한다.

3.2.1.Spring BlazeDS Integration 라이브러리

Spring Source Community에서 Spring BlazeDS Integration 1.0.1을 다운 받아 org.springframework.flex-1.0.1.RELEASE.jar파일을 Web Application Project의 {Web Root folder}/WEB-INF/lib에 복사한다.

또 Spring BlazeDS Integration과 Dependency관계에 있는 jackson-core-asl-x.x.x.jar을 다운로드페이지에서 다운 받아 Web Application Project의 {Web Root folder}/WEB-INF/lib에 복사한다.

3.2.2.Servlet 설정

Spring BlazeDS Integration 라이브러리 복사가 끝났으면, Flex UI에서 RPC요청이 왔을 때 MessageBrokerServlet이 아닌 Spring MVC의 DispatcherServlet이 요청을 처리 할 수 있도록 web.xml파일에 Servlet설정을 아래와 같이 추가 한다. 이 때 Spring MVC에 대한 Servlet설정이 이미 정의되어 있으면 새로운 Servlet을 추가 정의한다.

<servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/springmvc/common-servlet.xml,
                    classpath:/springmvc/generation-servlet.xml,	
        </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
	
<servlet>
    <servlet-name>SpringBlazeDS</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/springmvc/flex-servlet.xml
        </param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
</servlet>

위와 같이 SpringBlazeDS Servlet설정을 완료 한 후 아래와 같이 /messagebroker/*에 대한 요청을 SpringBlazeDS Servlet이 처리 할 수 있도록 servlet-mapping을 정의한다.

<servlet-mapping>
    <servlet-name>SpringBlazeDS</servlet-name>
    <url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>

Flex Client에서 RemotingService, HTTPProxyService, MessageService의 Request url 패턴은 /messagebroker/*형태이다.

3.2.3.Spring WebApplicationContext 설정

Web Appliaction의 classpath:/spring위치에 Flex관련 Spring WebApplicationContext설정 파일(flex.xml)을 생성한다.

flex-servlet.xml파일을 열어 flex namespace를 사용하기 위해 다음과 같이 xsd를 정의한다.

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:flex="http://www.springframework.org/schema/flex"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/flex 
	http://www.springframework.org/schema/flex/spring-flex-1.0.xsd">
		
	
</beans>

BlazeDS설정 파일인 services-config.xml파일을 읽어 MessageBrokerFactoryBean에 등록할 수 있도록 같이 flex namespace를 사용해 정의한다.

<flex:message-broker/>

위는 Default설정으로 services-config.xml파일이 classpath*:services-config.xml에 있을 경우에 사용할 수 있다. 추가적인 설정에 대해서는 Spring BlazeDS Integration Configuration을 참고한다.

3.3.Spring Bean Exporting

BlazeDS, Spring BlazeDS Integration의 설치가 끝났으면 Spring Bean을 Flex의 RemotingService가 접근 할 수 있도록 RemotingDestinationExporter에 등록한다. Spring Bean을 Exporting하기 위해서는 여러 방법이 있는데 여기에서는 @RemotingDestination annotation을 사용하도록 한다.

@Service("foundationGenreService")
@RemotingDestination
public class GenreServiceImpl implements GenreService {

	@Inject
	@Named("foundationGenreDao")
	private GenreDao genreDao;

	public List<Genre> getList() throws Exception {
		return genreDao.getList();
	}

}

@RemotingDestination annotation에 아무런 옵션을 주지 않으면 Bean Id가 Destination Id가 된다. 위의 소스코드에서는 foundationGenreService이 Destination Id가 되고 Flex Client에서는 foundationGenreService란 이름으로 해당 Service에 접근한다.

RemotingService가 접근 할 수 있도록 Spring Bean을 Exporting하는 자세한 방법은 Spring Blazeds Integration Configuration을 참고한다.

3.4.Flex Project 생성

Flex Web Application개발을 하기 위한 서버 측 설정이 완료 됐으면 Flex프로젝트를 생성한다. Flex UI를 개발하기 위해서는 편집기를 이용한 방법과 Flex Builder를 이용한 방법이 있다. Flex Builder는 Flex UI를 개발하기 위한 이클립스 기반의 상용 S/W 로 WYSIWYG Editor, mxml의 자동컴파일, 디버깅등 의 다양한 기능을 제공한다. 본 문서에서는 Flex Builder를 이용해 개발하는 방법에 대해서만 설명한다.

Adobe Flex Builder 3 Download에서 60일동안 사용가능한 Trial버전을 다운 받을 수 있다.

Flex Builder 3를 처음 실행 하게 되면 아래와 같은 화면을 볼 수 있다.

Flex Builder에서 File -> New -> Flex Project를 선택하면 새로운 Flex Project를 생성할 수 있다.

New Flex Project 창이 열리면 Project Name과 Flex Project의 Location등을 입력하고 Application type은 Web application(runs in Flash Player)을 선택, Server technology의 Application server type는 J2EE, Use remote object access service의 체크 박스를 선택, LiveCycle Data Services를 선택한 후 Next버튼을 클릭한다.

Configure J2EE Server창 Server location에서는 BlazeDS가 설치된 Web Application의 정보를 세팅한다. 서버 정보를 입력 한 후 Validate Configuration 버튼을 클릭해 BlazeDS가 설치 된 Web Application인지 확인한다.

Compiled Flex application location의 Output folder는 Flex Project가 컴파일 되서 위치할 경로를 입력한다. 일반적으로는 Web Application Project의 Web Root folder의 특정 폴더를 지정한다. 여기에서는 {Web Root folder}/flex를 Output folder로 설정했다. 입력이 끝났으면 Next버튼을 클릭한다.

다음으로는 Flex Project의 build path를 설정하는 창이 열리는데 Main application file에 Main Application mxml파일의 파일 이름을 입력한 후 Finish버튼을 클릭한다.

아래 그림과 같이 신규 프로젝트가 생성되고 Main Application mxml파일이 열리면 성공적으로 Flex Project가 생성된 것이다.

Web Application Project의 {Web Root folder}/flex폴더에 Flex Project가 컴파일 되어 아래와 같은 파일들이 생성된 것을 확인 할 수 있다. Flex Application Main Application mxml(main.mxml) 파일명(main)이 Flex Application 실행 html파일명(main.html)이 된다.

WAS를 시작해 Flex Application실행 html을 브라우저에서 호출한다.(http://localhost:8080/myproject/flex/main.html) 아래와 같이 화면이 출력 됐다면 Flex Project가 정상적으로 설치 된 것이다.

3.5.Remoting Service Call

Flex Applicatin Main mxml에서 Spring Bean(foundationGenreService)의 getList메소드를 호출한다. 아래와 같이 RemotingObject태그를 사용해 destination(Spring Bean)과 호출할 method를 정의한다.

<mx:RemoteObject id="remotingService" destination="foundationGenreService">
    <mx:method name="getList" result="resultHandler(event)"/>
</mx:RemoteObject>

foundationGenreService getList의 호출 결과는 resultHandler메소드에서 처리한다. 아래는 foundationGenreService getList메소드를 호출한 결과를 DataGird에 바인딩하는 예이다.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" initialize="getList()">
    <mx:Script>
        <![CDATA[
            import mx.rpc.events.ResultEvent;
            import mx.collections.ArrayCollection;
			
            [Bindable]
            private var genreList:ArrayCollection;
			
            private function getList():void{
                remotingService.getList();
            }
			
            private function resultHandler(event:ResultEvent):void{
                genreList = event.result as ArrayCollection;
            } 
        ]]>
    </mx:Script>
	
    <mx:RemoteObject id="remotingService" destination="foundationGenreService">
        <mx:method name="getList" result="resultHandler(event)"/>		
    </mx:RemoteObject>
   <mx:Array id="columnsInfo">
		<mx:DataGridColumn dataField="genreId" headerText="GENRE ID" editable="true"/>
		<mx:DataGridColumn dataField="name" headerText="NAME" editable="true"/>	
	</mx:Array>
	
	<mx:DataGrid id="grdGenre" dataProvider="{genreList}" columns="{columnsInfo}"/>
</mx:Application>

위와 같이 작성 한 후 main.html을 호출 하면 다음과 같은 화면을 볼 수 있다.

 

4.Spring BlazeDS Integration 환경 설정

Spring BlazeDS Integration은 Flex를 이용해 Web Application을 개발 할 경우, 서버측과 RPC방식으로 통신할 때 필요한 BlazeDS를 Spring Framework와 연계하여 편리하게 사용할 수 있도록 한다. 복잡한 환경 설정이 flex namespace를 통해 간편해 졌고 Spring Bean을 Exporting하기 위한 annotation이 제공된다.

Spring BlazeDS Integration을 사용하기 위한 환경은 다음과 같다.

  • Java 5 이상

  • Spring 2.5.6 이상

  • Adobe BlazeDS 3.2 이상

4.1.Spring BlazeDS MessageBroker 환경 설정

4.1.1.Spring DispatcherServlet

BlazeDS 에서는 MessageBrokerServlet 을 front Controller 로 사용하여 Flex client 의 요청을 처리했다. Spring BlazeDS Integration 을 사용하면 Spring MVC 의 DispatcherServlet 이 Flex client 의 요청을 처리한다. 따라서 web.xml 에 아래와 같은 설정을 추가한다.

<servlet>
    <servlet-name>SpringBlazeDS</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/springmvc/flex-servlet.xml</param-value>
    </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>SpringBlazeDS</servlet-name>
    <url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>

4.1.2.MessageBroker

Spring BlazeDS Integration에서는 BlazeDS와 Spring과의 연계를 위한 복잡한 환경 설정을 간단히 하기 위해 flex namespace를 제공한다. flex namespace를 사용하기 위해서는 Spring WebApplicationContext 설정 파일에 다음과 같이 xsd를 정의를 추가한다.

<?xml version="1.0" encoding="UTF-8"?>	
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:flex="http://www.springframework.org/schema/flex" 
    xmlns:security="http://www.springframework.org/schema/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/flex 
        http://www.springframework.org/schema/flex/spring-flex-1.0.xsd">
        
        ...
</bean>

web.xml 파일의 설정에 의해 flex client 의 요청을 DispatcherServlet 이 처리함으로 그 요청을 MessageBroker로 위임해야 한다.

MessageBroker 설정은 flex namespace 추가로 아래와 같이 간단한 설정만으로 가능하다.

<?xml version="1.0" encoding="UTF-8"?>	
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:flex="http://www.springframework.org/schema/flex" 
    xmlns:security="http://www.springframework.org/schema/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/flex 
        http://www.springframework.org/schema/flex/spring-flex-1.0.xsd">
			
    <flex:message-broker />
</beans>

위의 설정의 경우 기본적으로 /WEB-INF/flex/services-config.xml 파일을 참조하게 된다. 설정 파일의 위치가 다를 경우 services-config-path attribute 설정을 통해 변경이 가능하다.

<flex:message-broker services-config-path="classpath*:services-config.xml" />

flex namespace를 사용하지 않을 때에는 아래와 같이 설정한다.

<bean id="messageBroker" 
           class="org.springframework.flex.core.MessageBrokerFactoryBean">
    <property name=""serviceConfigPath" value="classpath*:services-config.xml"/>
</bean>

Configuring the Spring DispatcherServlet에서 처럼 /messagebroker/*요청에 대한 servlet-mapping을 별도로 설정하지 않고 모든 요청에 대해서 DispatcherServlet이 처리 할 경우 /messagebroker/*에 대한 요청을 MessageBroker가 처리 하기 위해서는 아래와 같이 SimpleUrlHandlerMapping을 설정한다.

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <value>
        /messagebroker/*=_messageBroker
        </value>
    </property>
</bean>

<bean class="org.springframework.flex.servlet.MessageBrokerHandlerAdapter"/>

위의 SimpleUrlHandlerMapping설정은 flex namespace를 사용할 경우는 아래와 같다.

<flex:message-broker>
    <flex:mapping pattern="/messagebroker/*" />
</flex:message-broker>

4.2.Exporting Spring Beans

4.2.1.RemotingService 환경 설정

Application 레벨의 채널 세팅은BlazeDS설정 파일인 services-config.xml파일에 services태그 안에 아래와 같이 정의 한다. Flex Client에서 RemotingService에 별도의 채널 설정이 없을 경우 my-amf채널을 이용한다.

<services>
    ..중략
    <default-channels>
        <channel ref="my-amf"/>
    </default-channels>        
</services>

RemotingService에 대해 특정 채널을 이용하고자 할 경우에는 아래와 같이 remoting-service의 default-channels속성에 채널 이름을 등록한다.

<flex:message-broker>
    <flex:remoting-service default-channels="my-amf, my-secure-amf" />
</flex:message-broker>

4.2.2.remoting-destination 태그

remoting-destination 태그는 Spring Bean 을 Remote 객체로 노출 시킨다.

<bean id="productService" class="flex.samples.product.ProductServiceImpl" />
<flex:remoting-destination ref="productService" />

다음과 같은 설정도 가능 하다.

<bean id="productService" class="flex.samples.product.ProductServiceImpl">
    <flex:remoting-destination />
</bean>

include-methods / exclude-methods 속성을 이용해 method 단위의 제어도 가능하다. include-method / exclude-method를 정의하지 않을 경우 default는 include-method이다.

<flex:remoting-destination ref="productService"
    include-methods="read, update"
    exclude-methods="create, delete"
    channels="my-amf, my-secure-amf" />

4.2.3.@RemotingDestination

@RemotingDestination 을 사용해서도 Spring Bean 을 Remote 객체로 노출이 가능하다.

@Service("productService")
@RemotingDestination
public class ProductServiceImpl implements ProductService {
..중략

@RemotingInclude, @RemotingExclude annotation을 이용해 method별 노출 여부를 설정할 수 있다.

@RemotingInclude
public Category getCategoryList(SearchVO searchVO) throws Exception{
..
}

@RemotingExclude
public void removeCategory(Category category) throws Exception{
...
}

@RemotingDestination annotaion을 이용해 Spring Bean을 노출 할 때 Destination 채널 설정은 아래와 같이 한다.

@Service("categoryService")
@RemotingDestination(channels={"my-amf","my-secure-amf"})
public class CategoryServiceImpl implements CategoryService {

 

Posted by 1010
반응형

출처 : http://blog.naver.com/PostView.nhn?blogId=thunder1119&logNo=100091204490

 

올해초 Srping BlazeDS Integration 1.0.0 M1 으로 스프링과 BlazeDS를 적용해 보았는데

( 머드초보님의 M1 사용기 : http://mudchobo.tomeii.com/tt/371 )

한학기 마치고 다시 접하려고 했더니 정식버전이 나왔더군요!

그래서 반가운 마음에 적용해보려고 Spring 홈페이지( http://www.springsource.org )에서 다운로드후

문서를 보았는데. M1에 비해서 문서의 양이 늘어나있고 영어 잼병인 저에겐 소스코드만이 유일한

해독가능한 언어인데 상황에 따라 다르게 적용해야되는 코드들이라 영 도움이 안되 몇일간 삽질을 시켜주었습니다ㅜㅜ

우선 설명하기에 앞서 플렉스 컴퍼넌트 카페( http://cafe.naver.com/flexcomponent.cafe ) 에 서기님이 올려주신

Flex3+eclipse europa + BlazeDS 셋팅 게시물의 첨부된 문서의 세팅방법을 모두 완료했다는 가정하에 설명하도록

하겠습니다. ( http://cafe.naver.com/flexcomponent.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=10906

서기님이 작성하신 셋팅문서 첨부해 두겠습니다.)



자 이제 위 과정으로 BlazeDS_Hello.mxml을 실행하였을 경우 "Welcome BlazeDS 성공!" 이란 문구를 보았다고 치고

이제 Srping BlazeDS Integration을 적용해 보도록 하겠습니다.

우선 Srping BlazeDS Integration에 필요한 환경(Java 5 or higher, Spring 2.5.6 or higher ,Adobe BlazeDS 3.2 or higher)

을 구성해주어야 되는데 http://www.springsource.org/download 에서 우선 Srping BlazeDS Integration과 Spring 2.5.6

을 받아서 lib안에 넣어줍니다.


그리고 web.xml을 수정해보겠습니다.

우선 spring을 사용하면서 필요없어진 리스너를 삭제합니다.

<!-- 아래의 리스너 삭제 -->
<listener>
        <listener-class>flex.messaging.HttpFlexSession</listener-class>
</listener>


그리고 스프링을 사용하기 위한 ContextLoaderListener를 삽입합니다.

<listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>


<servlet-mapping>태그는 그대로 두고 <servlet> 태그만 스프링을 사용하기 위해 아래와 같이 수정합니다.

<servlet>
        <servlet-name>MessageBrokerServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/config/web-application-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
</servlet>


여기 까지가 web.xml 수정사항입니다.

그 후 spring을 사용하려면 기본적으로 WEB-INF밑에 applicationContext.xml 파일이 있어야 하기에 만들어 줍니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
      
</beans>



 



이제 web.xml에서 <servlet>태그밑에 <param-value>에 선언해 두었던 web-application-config.xml을 만들어줍니다.

안의 내용은 뒤에 설명하도록 하겠습니다.


이제 WEB-INF밑에 flex폴더에 들어있는 BlazeDS관련 xml들을 수정하겠습니다.

우선 remoting-config.xml에 선언해둔 blaze id에 destination 태그를 삭제합니다. 이태그는 이제 spring 관리하에

web-application-config.xml에서 새로운 인생(?)을 살게 될 것입니다.

그리고 services-config.xml <services> 태그밑에  아래와 같이 default-channels 태그를 추가해 줍니다.

<services>
        <service-include file-path="remoting-config.xml" />
        <service-include file-path="proxy-config.xml" />
        <service-include file-path="messaging-config.xml" />   
        <default-channels>
           <channel ref="my-amf"/>
        </default-channels>     
    </services>


자 이제 주역인 web-application-config.xml에 내용을 넣도록 하겠습니다.

우선 설명하기에 앞서 전체코드 나갑니다.

<!-- web-application-config.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:flex="http://www.springframework.org/schema/flex"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
               http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
               http://www.springframework.org/schema/flex
            http://www.springframework.org/schema/flex/spring-flex-1.0.xsd">
       
       <bean id="mySpringManagedMessageBroker" class="org.springframework.flex.core.MessageBrokerFactoryBean" />

       <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"
             p:mappings="/*=mySpringManagedMessageBroker" />
      
       <bean class="org.springframework.flex.servlet.MessageBrokerHandlerAdapter" />
                
       <bean id="test" class="test.Test">
               <flex:remoting-destination destination-id="blaze" message-broker="mySpringManagedMessageBroker"/>
       </bean>

</beans>



얼핏 복잡해보이기도 하지만 사실 그렇게 어렵지 않습니다.

우선 네임스페이스 부분을 보면 xmlns:flex="http://www.springframework.org/schema/flex 추가됬고

스키마부분을 보면

http://www.springframework.org/schema/flex
http://www.springframework.org/schema/flex/spring-flex-1.0.xsd

기존 스프링에서 이두개가 추가되었고

web.xml에서 들어오는 MessageBroker를 아래 태그가 담당합니다.

<bean id="mySpringManagedMessageBroker" class="org.springframework.flex.core.MessageBrokerFactoryBean" />


id는 편하신데로 주시면 되고 사실 이부분을 doc에 보면 여러가지 방법으로 표현할 수 있는데 <flex:message-broker/>등

짧게 표현이 가능하지만 doc이 부실한지 제가 못찾는지 이대로 실행하면 오류가나서 실행이안됩니다.

2009/07/13 추가사항

<flex:message-broker/> 방식의 사용법을 보시려면 다음의 페이지를 참고해주세요!

http://actionscripter.tistory.com/27



그래서 전 그냥 기존 spring 방식으로 bean으로 처리했습니다.

다음으로 스프링에 SimpleUrlHandlerMapping 으로 MassageBroker와 연결합니다.

저는 편의상 p 네임스페이스로 축약해서 사용했습니다.

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"
             p:mappings="/*=mySpringManagedMessageBroker" />

또는

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/messagebroker/*=mySpringManagedMessageBroker
</value>
</property>
</bean>


그리고 이부분은 저도 잘 모르겠는데 messageBroker와 관련된 adapter인듯 합니다.

<bean class="org.springframework.flex.servlet.MessageBrokerHandlerAdapter" />


그리고 저를 가장 고생시킨.. remoting 부분인데 1.0.0 정식이 나오면서 <flex:remoting-service>태그가

<flex:remoting-destination>으로 바겼는지 국내와 국외문서 모두에 <flex:remoting-service>으로 되어있어서

상당히 고생했습니다. 이부분도 다양한 사용방법이 있는데 자세한것은 레퍼런스를 참조하시고 우선 성공한 방법만

소개하겠습니다.

<bean id="test" class="test.Test">
               <flex:remoting-destination destination-id="blaze" message-broker="mySpringManagedMessageBroker"/>
</bean>

또는

<bean id="test" class="test.Test"/>
      
<flex:remoting-destination ref="test" destination-id="blaze" message-broker="mySpringManagedMessageBroker"/>

또는

<bean id="test" class="test.Test"/>

<bean id="product" class="org.springframework.flex.remoting.RemotingDestinationExporter"
             p:messageBroker-ref="mySpringManagedMessageBroker" p:service-ref="test"
             p:destinationId="blaze" />

레퍼런스문서에 ref 외에는 나와있질 않아서 계속 메시지브로커를 찾지못한다는 에러를 보았는데

결국 flex스키마 문서를 죄 뒤져서 속성을 찾아냈습니다 ㅡㅠ

message-broker="mySpringManagedMessageBroker" 부분은 꼭 넣어주시고

remoting-config.xml에서 destination id로 사용되는 부분과 같은 속성이 destination-id 입니다.

사실 이외에도 채널등 여러 속성이 있는데 불필요하다고 판단해서 다 제외했습니다.

자이제 여기까지 설정을 하시고 BlazeDS_Hello.mxml을 실행해보시면 다음과 같은 아름다운 화면을 보실 수 있습니다!


 

Posted by 1010
반응형

Spring Projects

Spring Flex

Spring BlazeDS Integration is a top-level Spring project, and a component of the complete Spring Web stack.  This project's purpose is to make it easier to build Spring-powered Rich Internet Applications using Adobe Flex as the front-end client.  It aims to achieve this purpose by providing first-class support for using the open source Adobe BlazeDS project and its powerful remoting and messaging facilities in combination with the familiar Spring programming model.

The Flex Addon for Spring Roo is a new component that raises the bar for productivity in building Flex clients backed by Spring BlazeDS Integration on the server-side. By utilizing the next-generation code-generation and management facilities of Spring Roo, it provides the fastest way to get up and running with a new Spring-based Flex project by letting developers focus on doing what they do best - write code.

 

Spring BlazeDS Integration is a top-level Spring project, and a component of the complete Spring Web stack.  This project's purpose is to make it easier to build Spring-powered Rich Internet Applications using Adobe Flex as the front-end client.  It aims to achieve this purpose by providing first-class support for using the open source Adobe BlazeDS project and its powerful remoting and messaging facilities in combination with the familiar Spring programming model.

The Flex Addon for Spring Roo is a new component that raises the bar for productivity in building Flex clients backed by Spring BlazeDS Integration on the server-side. By utilizing the next-generation code-generation and management facilities of Spring Roo, it provides the fastest way to get up and running with a new Spring-based Flex project by letting developers focus on doing what they do best - write code.

#maven

Latest News

Spring Flex Resources

Spring Flex Presentations

Spring Flex Articles and Tutorials

 

Maven Artifacts

Here is the Spring External Repository needed for BlazeDS dependencies:

<repository>
<id>spring-external</id>
<name>Spring External Repository</name>
<url>http://maven.springframework.org/external</url>
</repository>

Here is the Spring Flex dependency definition:

<dependency>
<groupId>org.springframework.flex</groupId>
<artifactId>spring-flex-core</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>

 

Posted by 1010
반응형
Posted by 1010
반응형

첨부한 파일을 다운 받아서 프로젝트에 넣어주시구요.

(출처 : http://flexology.wordpress.com/2008/09/30/loadinganimated_gif_in_flex/ )

AnimatedGIFImage.as 파일을 추가합니다.
소스는 아래와 같습니다.

package gif
{
 import flash.net.URLRequest;

 import mx.controls.Image;
 import mx.core.UIComponent;

 import org.gif.player.GIFPlayer;

 public class AnimatedGIFImage extends Image
 {
  private var _gifImage:UIComponent;

  public function AnimatedGIFImage()
  {
   super();
   this._gifImage=new UIComponent();
  }

  override public function set source(value:Object):void
  {
   if (!value is String)
   {
    throw new ArgumentError("Source must be of type String");
   }

   super.source=value;
  }

  override protected function createChildren():void
  {
   super.createChildren();
   var player:GIFPlayer=new GIFPlayer();
   player.load(new URLRequest(this.source as String));
   this._gifImage.addChild(player);
  }

  override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
  {
   this.addChild(this._gifImage);
   super.updateDisplayList(unscaledWidth, unscaledHeight);
  }
 }
}

그리고 사용하실땐
<gif:AnimatedGIFImage source="test.gif" width="100" height="100"/> 와 같이 사용하시면 됩니다.

Posted by 1010
반응형
Tree Icon Spinner (not using animated gif)
I've been disappointed that Flex doesn't natively support animated gifs. I especially wanted to use them inside Flex Trees when I'm loading the children dynamically. So instead of trying to get animated gifs to work (see this example if you are interested) I created a relatively simple Spinner class that draws a circular spinner using just Flex Graphics. The spinner is animated using a Timer. If you set the startImmediately property to true then the animation starts when the spinner component is added to a parent component. And it is always stopped when it gets removed from its parent.
There are also the start(), stop() and running functions to control the spinner manually.

The spinners have a few styles that you can set to customize the appearance:
<ui:Spinner
  Properties
delay="100"
startImmediately="false"
Styles
backgroundAlpha="1"
backgroundColor="NaN"
spinnerColor="#829AD1"
spinnerHighlightColor="#001A8E"
spinnerThickness="3"
spinnerLineThickness="2"
spinnerType="gradientcircle"/>

I've created an example of the four types of spinners in four Trees. Each tree node loads its children after a 2 second delay during which time the spinner is shown, so try expanding each tree (right click to view source):


Let me know if you like them!

Also, make sure you do call stop() on the spinner when you don't need it anymore... otherwise you might notice your Flex app starts to run very slowly like mine did...

 

Posted by 1010
반응형

Datagrid avec ItemRenderer avec CheckBox, ComboBox, ColorPicker

Updated for Flex 3

Voici un exemple d'utilisation du composant DataGrid en Flex 2 Flex 3 avec des ItemRenderer.

Chaque champ peut-être édité. Cet exemple montre l'utilisation de CheckBox, de ComboBox, de NumericStepper et de ColorPicker comme Renderer.

Voir la démo - See the demo

Voir les sources - View the sources

Obtenir les sources dgRendererSimple.zip

DataGrid avec ItemRenderer

Explication

Tout est dans le code.

Les ItemRenderer de chaque colonne sont programmés en MXML.

Posted by 1010
반응형

Progressbar in Datagrid Example

Line Break

Author: Roelof (13 Articles) - Author Website

Roelof is a SAP Consultant specialized in Front-End development. In his spare free time he is either developing on the web, playing basketball, watching soccer or traveling. He is also the co-owner of Flex-Blog.com.


In the comments of our previous post about using the ProgressBar, one of our readers asked if a ProgressBar could be used inside a DataGrid.

This is, ofcourse, possible. So we decided to write an example about this.

There are a couple of things you need to know before we start:

  1. Use an ItemRenderer to show the ProgressBar inside the DataGrid instead of text.
  2. Create a DataProvider for the DataGrid (in our case we use an ArrayCollection).
  3. Create an action to start the ProgressBar (in our case we simulate a download).
  4. Make sure the ArrayCollection is updated on every progress of the ProgressBar.

First we create an ItemRenderer in a seperate ActionScript file (myProgressBar.as):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package
{
    import mx.containers.HBox;
    import mx.controls.ProgressBar;
    import mx.controls.dataGridClasses.*;

    public class myProgressBar extends HBox
    //This should normally be extends ProgressBar, we are using HBox to have more control over the layout.
    {
        private var pb:ProgressBar;
       
        public function myProgressBar():void {
            //Create new ProgressBar Instance
            pb = new ProgressBar();
            //Set some layout things
            pb.mode = "manual";
            pb.percentWidth = 100;         
            pb.labelPlacement = "center";
            this.setStyle("verticalAlign","middle");
            //Add ProgressBar as child
            addChild(pb);
        }
       
        override public function set data(value:Object):void
        {
            super.data = value;
        }  
       
        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number) : void{
            super.updateDisplayList(unscaledWidth, unscaledHeight);
            pb.setProgress(data.loaded, data.total);
        }
    }
}

Then we will use this ItemRenderer inside the DataGrid column.

1
<mx:DataGridColumn itemRenderer="myProgressBar" dataField="progress" headerText="Progress" width="180" paddingLeft="5"/>

To start the ‘fake’ ProgressBar download progress, we are using an Image as a Button inside the DataGrid.

1
2
3
4
5
6
7
8
9
10
            <mx:DataGridColumn width="112" headerText="Download">
                <mx:itemRenderer>
                    <fx:Component>
                        <mx:HBox horizontalAlign="center" verticalAlign="middle">    
                            <s:Label text="{data.file}"/>
                            <mx:Image buttonMode="true" toolTip="'Download'" click="outerDocument.downloadFile()" source="@Embed(source='images/down_alt.png')"/>
                        </mx:HBox>
                    </fx:Component>
                </mx:itemRenderer>             
            </mx:DataGridColumn>

Why use outerDocument?
Since the itemRenderer is within another Component, we need to use outerDocument to call a method inside your ‘main’ Component.

Next step was to implement the method downloadFile():

1
2
3
4
5
6
7
            public function downloadFile():void{
                //start timer
                var timer:Timer = new Timer(500);
                // add event listener
                timer.addEventListener(TimerEvent.TIMER, updateProgressBar);
                timer.start();             
            }

Next, implement the eventListener. Inside the eventListener we need to make sure the DataGrid / ArrayCollection gets updated.

1
2
3
4
5
6
7
            private function updateProgressBar(event:TimerEvent):void{
                var myItem:Object
                // Add a 'random' number to loaded. To fake the progress..
                myDataGrid.selectedItem.loaded += Math.ceil(Math.random() * 5);
                //refesh arraycollection to refresh the datagrid
                myArrayCollection.refresh();               
            }

Then you are done. Below you will find a working example. For the full source code either use Right Mouse Click -> View Source or scroll down.

Full Source Code:
ProgressBarInsideDatagridExample.mxml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?xml version="1.0" encoding="utf-8"?>
<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" width="500"  height="200" initialize="init();" viewSourceURL="srcview/index.html">

    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
           
            [Bindable]private var myArrayCollection:ArrayCollection;
           
            private function init():void {
                // Create instance of myArrayCollection
                myArrayCollection = new ArrayCollection;
                // Create new Object
                var obj:Object = new Object;
               
                obj.file = "File 1";
                obj.total = 100;
                obj.loaded = 0;
                // Add object to myArrayCollection
                myArrayCollection.addItem(obj);
                obj = new Object;
                obj.file = "File 2";
                obj.total = 100;
                obj.loaded = 0;
                // Add object to myArrayCollection
                myArrayCollection.addItem(obj);
            }

            public function downloadFile():void{
                //start timer
                var timer:Timer = new Timer(500);
                // add event listener
                timer.addEventListener(TimerEvent.TIMER, updateProgressBar);
                timer.start();             
            }

            private function updateProgressBar(event:TimerEvent):void{
                var myItem:Object
                // Add a 'random' number to loaded. To fake the progress..
                myDataGrid.selectedItem.loaded += Math.ceil(Math.random() * 5);
                //refesh arraycollection to refresh the datagrid
                myArrayCollection.refresh();               
            }
           
           
        ]]>
    </fx:Script>
   
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->    
    </fx:Declarations>
   
    <mx:DataGrid id="myDataGrid" height="200" width="500" dataProvider="{myArrayCollection}">
        <mx:columns>
            <mx:DataGridColumn width="112" headerText="Download">
                <mx:itemRenderer>
                    <fx:Component>
                        <mx:HBox horizontalAlign="center" verticalAlign="middle">    
                            <s:Label text="{data.file}"/>
                            <mx:Image buttonMode="true" toolTip="'Download'" click="outerDocument.downloadFile()" source="@Embed(source='images/down_alt.png')"/>
                        </mx:HBox>
                    </fx:Component>
                </mx:itemRenderer>             
            </mx:DataGridColumn>
           
            <mx:DataGridColumn itemRenderer="myProgressBar" dataField="progress" headerText="Progress" width="180" paddingLeft="5"/>
        </mx:columns>          
    </mx:DataGrid>
   
</s:Application>

myProgressBar.as

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package
{
    import mx.containers.HBox;
    import mx.controls.ProgressBar;
    import mx.controls.dataGridClasses.*;

    public class myProgressBar extends HBox
    //This should normally be extends ProgressBar, we are using HBox to have more control over the layout.
    {
        private var pb:ProgressBar;
       
        public function myProgressBar():void {
            //Create new ProgressBar Instance
            pb = new ProgressBar();
            //Set some layout things
            pb.mode = "manual";
            pb.percentWidth = 100;         
            pb.labelPlacement = "center";
            this.setStyle("verticalAlign","middle");
            //Add ProgressBar as child
            addChild(pb);
        }
       
        override public function set data(value:Object):void
        {
            super.data = value;
        }  
       
        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number) : void{
            super.updateDisplayList(unscaledWidth, unscaledHeight);
            pb.setProgress(data.loaded, data.total);
        }
    }
}

 

Posted by 1010
반응형

TabNavigator와 ModuleLoader 사용시 문제

TabNavigator 가 포함된 상위 노드에 creationPolicy="all" 로 해주면 어느정도 문제는 해결됨

Posted by 1010
반응형

Copy Flex UIComponent

I was trying to copy a canvas(UIComponent), but realize that it’s not a simple job as it looks like.

1. One can use the ObjectUtil.copy()..method to copy a simple object, but we can not use the same for UIComponent.

2. Secondly,  one can use Bitmap to copy the Image type. Like…

public static function cloneImage(image:Image):Image
          {
               var clone:BitmapData = Bitmap(image.source).bitmapData.clone();
               var bitmap:Bitmap           = new Bitmap(clone);
               
               var newImage:Image          = new Image();
               newImage.source           = bitmap;
               
               return newImage;
          }
3.Thirdly, we can use the componentDescriptor to create a container

Like..

 <?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml&#8221; layout=”horizontal”>
     <mx:Script>
          <![CDATA[
               private function cloneButton_clickHandler (event:MouseEvent):void
               {
                    var childDescriptors:Array = original.childDescriptors;
                    var descriptorsCount:int = childDescriptors.length;
                    for (var i:int; i < descriptorsCount; i++)
                    {
                         cloned.createComponentFromDescriptor(original.childDescriptors[i],
                                                                       false);
                    }
                    cloned.validateNow();
               }
          ]]>

     </mx:Script>
     <mx:Button label=”Clone Canvas” click=”cloneButton_clickHandler(event);” />
     <mx:Canvas id=”original” width=”100″ height=”100″ borderStyle=”solid” >
          <mx:Label text=”Some Label” />
          <mx:ComboBox dataProvider=”{['data1', 'data2']}” bottom=”0″ />
     </mx:Canvas>
     <mx:Canvas id=”cloned” width=”100″ height=”100″ borderStyle=”solid” >
          
     </mx:Canvas>
</mx:Application>

but, still there is a problem in the above method. It copy only the first level children.

For more usefulness we need to make RECURSIVE call to this method to copy each and every children it has which is really frustating…:(

4. I also tried to implement this by using Reflection API or taking a snapshot of the current view…

But, unfortunately  flex does not have snapshot method. I guess we need to take help of ExternalInterface to implement the sanpshot method..

After going through all this I find that flex should have make this more simplify by providing an interface to clone any UICompoent.

Sorry to say, I guess flex is really lagging in this regard…

 

Posted by 1010
반응형

flex DataGrid Border remove

화면 전환시 데이터그리드 외곽선이 지워지는 현상

variableRowHeight="true"

Posted by 1010
반응형
Package flash.printing
Class public final class PaperSize
Inheritance PaperSize Inheritance Object

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

 

This class provides the available values for the paperSize parameter of the PrintJob.selectPaperSize() method. Each constant represents a paper size that is used to print a page.

The following table shows the approximate size for each paper type. The size is approximate because there is some variation among printer drivers. For example, the width of A4 paper can be 595.0, 595.2, 595.22 or 595.28 points depending on the driver.

Value Size in points
A4 595 x 842
A5 420 x 595
A6 297 x 420
CHOUKEI3GOU 340 x 666
CHOUKEI4GOU 298 x 666
ENV_10 297 x 684
ENV_B5 499 x 709
ENV_C5 459 x 649
ENV_DL 312 x 624
ENV_MONARCH 279 x 540
ENV_PERSONAL 261 x 468
EXECUTIVE 522 x 756
FOLIO 612 x 936
JIS_B5 516 x 729
LEGAL 612 x 1008
LETTER 612 x 792
STATEMENT 396 x 612

 

Related API Elements



Public Properties
  Property Defined By
  Inherited constructor : Object
A reference to the class object or constructor function for a given object instance.
Object
  Inherited prototype : Object
[static] A reference to the prototype object of a class or function object.
Object
Public Methods
  Method Defined By
  Inherited
Indicates whether an object has a specified property defined.
Object
  Inherited
Indicates whether an instance of the Object class is in the prototype chain of the object specified as the parameter.
Object
  Inherited
Indicates whether the specified property exists and is enumerable.
Object
  Inherited
Sets the availability of a dynamic property for loop operations.
Object
  Inherited
Returns the string representation of this object, formatted according to locale-specific conventions.
Object
  Inherited
Returns the string representation of the specified object.
Object
  Inherited
Returns the primitive value of the specified object.
Object
Public Constants
  Constant Defined By
        A4 : String = "a4"
[static] A4
PaperSize
        A5 : String = "a5"
[static] A5
PaperSize
        A6 : String = "a6"
[static] A6
PaperSize
        CHOUKEI3GOU : String = "choukei3gou"
[static] Japanese choukei 3 gou (envelope)
PaperSize
        CHOUKEI4GOU : String = "choukei4gou"
[static] Japanese choukei 4 gou (envelope)
PaperSize
        ENV_10 : String = "env_10"
[static] Legal envelope
PaperSize
        ENV_B5 : String = "env_b5"
[static] B5 envelope
PaperSize
        ENV_C5 : String = "env_c5"
[static] C5 envelope
PaperSize
        ENV_DL : String = "env_dl"
[static] DL envelope
PaperSize
        ENV_MONARCH : String = "env_monarch"
[static] Monarch envelope
PaperSize
        ENV_PERSONAL : String = "env_personal"
[static] Personal envelope
PaperSize
        EXECUTIVE : String = "executive"
[static] Executive size
PaperSize
        FOLIO : String = "folio"
[static] Folio size
PaperSize
        JIS_B5 : String = "jis_b5"
[static] Japanese B5
PaperSize
        LEGAL : String = "legal"
[static] Traditional legal size
PaperSize
        LETTER : String = "letter"
[static] Traditional letter size
PaperSize
        STATEMENT : String = "statement"
[static] Statement size
PaperSize
Constant Detail
    

A4

Constant
public static const A4:String = "a4"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

A4

    

A5

Constant  
public static const A5:String = "a5"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

A5

    

A6

Constant  
public static const A6:String = "a6"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

A6

    

CHOUKEI3GOU

Constant  
public static const CHOUKEI3GOU:String = "choukei3gou"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

Japanese choukei 3 gou (envelope)

    

CHOUKEI4GOU

Constant  
public static const CHOUKEI4GOU:String = "choukei4gou"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

Japanese choukei 4 gou (envelope)

    

ENV_10

Constant  
public static const ENV_10:String = "env_10"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

Legal envelope

    

ENV_B5

Constant  
public static const ENV_B5:String = "env_b5"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

B5 envelope

    

ENV_C5

Constant  
public static const ENV_C5:String = "env_c5"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

C5 envelope

    

ENV_DL

Constant  
public static const ENV_DL:String = "env_dl"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

DL envelope

    

ENV_MONARCH

Constant  
public static const ENV_MONARCH:String = "env_monarch"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

Monarch envelope

    

ENV_PERSONAL

Constant  
public static const ENV_PERSONAL:String = "env_personal"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

Personal envelope

    

EXECUTIVE

Constant  
public static const EXECUTIVE:String = "executive"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

Executive size

    

FOLIO

Constant  
public static const FOLIO:String = "folio"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

Folio size

    

JIS_B5

Constant  
public static const JIS_B5:String = "jis_b5"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

Japanese B5

    

LEGAL

Constant  
public static const LEGAL:String = "legal"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

Traditional legal size

    

LETTER

Constant  
public static const LETTER:String = "letter"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

Traditional letter size

    

STATEMENT

Constant  
public static const STATEMENT:String = "statement"

 

Language Version:  ActionScript 3.0
Runtime Versions:  AIR 2

 

 

Statement size


 

Posted by 1010
반응형
Printing

Adobe® Flash® Player and Adobe® AIR™ can communicate with an operating system’s printing interface so that you can pass pages to the print spooler. Each page Flash Player or AIR sends to the spooler can contain content that is visible, dynamic, or offscreen to the user, including database values and dynamic text. Additionally, Flash Player and AIR set the properties of the flash.printing.PrintJob class based on a user’s printer settings, so that you can format pages appropriately.

This chapter details strategies for using the flash.printing.PrintJob class methods and properties to create a print job, read a user’s print settings, and make adjustments to a print job based on feedback from Flash Player or AIR and the user’s operating system.

 

Posted by 1010
반응형

In the first article of this series, I discussed how to prevent users from printing directly from Flash’s default context menu. In the second article, I wrote about using the AlivePDF library to create PDFs from your application for printing or saving. Today I will be covering how to print application data from Flex.

The Flex framework comes with several classes to assist you in printing; however, they don’t tend to produce the greatest results. The PrintDataGrid class is probably your best option when it comes to printing data, but whenever I’ve worked with it in the past I’ve always ended up disappointed. Even after formatting, there are always issues; like rows getting cut off and pages breaks in weird places.

There have been talks about AlivePDF offering a Grid class to the library, which may be interesting, but it hasn’t been released and would probably only handle DataGrid data. So the best option I’ve found for printing data is to allow a server to generate a PDF, which can then be sent back to Flex and output however you want.

A great benefit of this option is the ability to easily format the print results. You can use simple HTML and CSS to customize the printout much easier than you could using ActionScript. You can also add custom styles, letterheads, and footers to fit your needs.

Here is an example, you can view the source here.

This will work with any server-side script that can generate PDF content. In this demo I am using a ColdFusion script which simply generates an HTML table and returns it in PDF format. You can view the ColdFusion code along with the rest of the source code here.

The data from the server is returned to Flex as a ByteArray. You can take that ByteArray and output it to the user in several different ways:

Using the Browser

Similar to the last article in this series, you can send the results to a server-side script to either display the PDF in the browser or prompt the user to open or save the document. This time, however, the AlivePDF library is no longer necessary since the PDF binary has already been created.

private function generatePDF(pdfBinary:ByteArray, method:String):void{

 

//result comes back as binary, create a new URL request and pass it back to the server
var header:URLRequestHeader = new URLRequestHeader("Content-type", "application/octet-stream");
var urlString:String = "http://kalengibbons.com/assets/pages/pdfCreator.cfm";
if(method == "inline")

 

urlString += "?method=inline";

else

urlString += "?method=attachment&name=dataPrintSample.pdf";

var sendRequest:URLRequest = new URLRequest(urlString);
sendRequest.requestHeaders.push(header);
sendRequest.method = URLRequestMethod.POST;
sendRequest.data = pdfBinary;
navigateToURL(sendRequest, "_blank");

}

Save locally from Flash

Thanks to new capabilities in Flash Player 10, you can also provide users the option to save the PDF directly from Flash. This eliminates the need for the second server-side script.

private function savePDF(pdfBinary:ByteArray):void{

 

var fileRef:FileReference = new FileReference();
fileRef.save(pdfBinary, "yourPrintout.pdf");

}

If you plan to use the save functionality you’ll need to set the “Required Flash Player version” in your project preferences to 10.0.0 or higher or else Flex Builder will complain. You have to remember that only users with Flash Player 10 or above will be able to use this functionality, so some type of version validation is a necessity.

Another caveat is that the save method must be triggered by some type of user interaction, that’s why the sample application prompts for system access before saving. Although the user has clicked the “Print Data” button, that event is lost when the server call is made, so we need another interaction from the user.

 

Posted by 1010
반응형
Posted by 1010
반응형

Using the FlexPrintJob class

You use the FlexPrintJob class to print one or more Flex objects, such as a Form or VBox container. For each object that you specify, Flex prints the object and all objects that it contains. The objects can be all or part of the displayed interface, or they can be components that format data specifically for printing. The FlexPrintJob class lets you scale the output to fit the page, and automatically uses multiple pages to print an object that does not fit on a single page.

You use the FlexPrintJob class to print a dynamically rendered document that you format specifically for printing. This capability is especially useful for rendering and printing such information as receipts, itineraries, and other displays that contain external dynamic content, such as database content and dynamic text.

You often use the FlexPrintJob class within an event listener. For example, you can use a Button control with an event listener that prints some or all of the application.

Note: The FlexPrintJob class causes the operating system to display a Print dialog box. You cannot print without some user action.

 

Build and send a print job

You print output by building and sending a print job.

  1. Create an instance of the FlexPrintJob class:
    var printJob:FlexPrintJob = new FlexPrintJob();
    
    
  2. Start the print job:
    printJob.start(); 
    
    

    This causes the operating system to display a Print dialog box.

  3. Add one or more objects to the print job and specify how to scale them:
    printJob.addObject(myObject, FlexPrintJobScaleType.MATCH_WIDTH); 
    
    

    Each object starts on a new page.

  4. Send the print job to the printer:
    printJob.send(); 
    
    
  5. Free up any unneeded objects.

Note: Because you are spooling a print job to the user's operating system between your calls to the start() and send() methods, you should limit the code between these calls to print-specific activities. For example, the content should not interact with the user between the start() and send() methods.

 

The following sections detail the procedures to use in these steps.

Starting a print job

To start a print job, you create an instance of the FlexPrintJob class and call its start() method. This method prompts Flash Player or Adobe® AIR™ to spool the print job to the user's operating system, which causes the user's operating system to display a Print dialog box.

If the user selects an option to begin printing from the Print dialog box, the start() method returns a value of true. If the user cancels the print job, the return value is false. After the user exits the operating system Print dialog box, the start() method uses the printer information to set values for the FlexPrintJob object's pageHeight and pageWidth properties, which represent the dimensions of the printed page area.

Note: Depending on the user's operating system, an additional dialog box might appear until spooling is complete and the application calls the send() method.

 

Only one print job can be active at a time. You cannot start a second print job until one of the following has happened with the previous print job:

  • The start() method returns a value of false (the job failed).
  • The send() method completes execution following a successful call to the addObject() method. Because the send() method is synchronous, code that follows it can assume that the call completed successfully.

Adding objects to the print job

You use the addObject() method of the FlexPrintJob class to add objects to the print job. Each object starts on a new page; therefore, the following code prints a DataGrid control and a Button control on separate pages:

printJob.addObject(myDataGrid);
printJob.addObject(myButton);

Scaling a print job

The scaleType parameter of the addObject() method determines how to scale the output. Use the following FlexPrintJobScaleType class constants to specify the scaling method:

Constant

Action

MATCH_WIDTH

(Default) Scales the object to fill the available page width. If the resulting object height exceeds the page height, the output spans multiple pages.

MATCH_HEIGHT

Scales the object to fill the available page height. If the resulting object width exceeds the page width, the output spans multiple pages.

SHOW_ALL

Scales the object to fit on a single page, filling one dimension; that is, it selects the smaller of the MATCH_WIDTH or MATCH_HEIGHT scale types.

FILL_PAGE

Scales the object to fill at least one page completely; that is, it selects the larger of the MATCH_WIDTH or MATCH_HEIGHT scale types.

NONE

Does not scale the output. The printed page has the same dimensions as the object on the screen. If the object height, width, or both dimensions exceed the page width or height, the output spans multiple pages.

If an object requires multiple pages, the output splits at the page boundaries. This can result in unreadable text or inappropriately split graphics. For information on how to format your print job to avoid these problems, see Printing multipage output.

The FlexPrintJob class includes two properties that can help your application determine how to scale the print job. These properties are read-only and are initially 0. When the application calls the start() method and the user selects the Print option in the operating system Print dialog box, Flash Player or AIR retrieves the print settings from the operating system. The start() method populates the following properties:

Property

Type

Unit

Description

pageHeight

Number

Points

Height of the printable area on the page; does not include any user-set margins.

pageWidth

Number

Points

Width of the printable area on the page; does not include any user-set margins.

Note: A point is a print unit of measurement that is 1/72 of an inch. Flex automatically maps 72 pixels to one inch (72 points) of printed output, based on the printer settings.

 

Completing the print operation

To send the print job to a printer after using the FlexPrintJob addObject() method, use the send() method, which causes Flash Player or AIR to stop spooling the print job so that the printer starts printing.

After sending the print job to a printer, if you use print-only components to format output for printing, call removeChild() to remove the print-specific component. For more information, see Using a print-specific output format.

Example: A simple print job

The following example prints a DataGrid object exactly as it appears on the screen, without scaling:

<?xml version="1.0"?>
<!-- printing\DGPrint.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">

    <mx:Script>
        <![CDATA[
            import mx.printing.*;

            // Create a PrintJob instance.
            private function doPrint():void {
                // Create an instance of the FlexPrintJob class.
                var printJob:FlexPrintJob = new FlexPrintJob();

                // Start the print job.
                if (printJob.start() != true) return;

                // Add the object to print. Do not scale it.
                printJob.addObject(myDataGrid, FlexPrintJobScaleType.NONE);

                // Send the job to the printer.
                printJob.send();
            }
        ]]>
    </mx:Script>

    <mx:VBox id="myVBox">
        <mx:DataGrid id="myDataGrid" width="300">
            <mx:dataProvider>
                <mx:Object Product="Flash" Code="1000"/>
                <mx:Object Product="Flex" Code="2000"/>
                <mx:Object Product="ColdFusion" Code="3000"/>
                <mx:Object Product="JRun" Code="4000"/>
            </mx:dataProvider>
        </mx:DataGrid>
        <mx:Button id="myButton" 
            label="Print" 
            click="doPrint();"/>
    </mx:VBox>
</mx:Application>

The executing SWF file for the previous example is shown below:

 

In this example, selecting the Button control invokes the doPrint() event listener. The event listener creates an instance of the FlexPrintJob class to print the DataGrid control, adds the DataGrid control to the print job using the addObject() method, and then uses the send() method to print the page.

To print the DataGrid and Button controls on your page, specify myVBox, the ID of the object that contains both controls, in the addObject() method's object parameter. If want to print the DataGrid and Button controls on separate pages, specify each object in a separate addObject() method.

To print the DataGrid control so that it spans the page width, omit the second addObject() method parameter, or specify FlexPrintJobScaleType.MATCH_WIDTH. To print the DataGrid control with the largest size that fits on a single page, specify FlexPrintJobScaleType.SHOW_ALL. In the previous example, FlexPrintJobScaleType.SHOW_ALL has the same result as FlexPrintJobScaleType.MATCH_WIDTH because the DataGrid is short.

Posted by 1010
반응형

Flex printing breakthrough! And more!

Hi guys. It’s occurred to me that this blog is wandering off topic. It’s not cheap pr0n, nor is it really supposed to be about feelings and girly rubbish… but sober, hard core technical discussions. Yes, I know cinnkitty finds those a turn on, but she also likes footy and beer.

Today is all about printing in Flex and AIR. While there is a fair amount of information out there about printing in Flex/AIR, the fact remains that at the end of the day, it’s not really designed for printing. The output is, I’ve found, unreliable and  fuzzy, essentially an image of the print area (sometimes with the scroll bars showing!), rather than a true print.

An example is this:

// AS3 code:

public textArea1:TextArea = new TextArea();

public function printText(e:Event = null):void{
trace(‘printing Plain Text’)
textArea1.htmlText = stringToPrint;//this is where the string to print goes                                                                                     textArea1.width = 800;
textArea1.height = 1000;
textArea1.visible = false;
var timer : Timer = new Timer( 1000, 1 );
timer.addEventListener(TimerEvent.TIMER, function():void{
try{
var PrintJob:FlexPrintJob = new FlexPrintJob;
PrintJob.start()
PrintJob.printAsBitmap=false;
PrintJob.addObject(Browser, FlexPrintJobScaleType.SHOW_ALL)
PrintJob.send()
PrintJob = null
Browser.height = 0;
Browser.width = 0;
}catch(e:Error){
//
}
} );
timer.start();
}

//end AS3 code

which  is fine for simple text, up to a point can even parse html a bit. Images? think again… More than a few pages? You’re joking, right?

The answer is actually on it’s way, in the form of http://www.alivepdf.org/

This guy has figured out a way of  constructing a PDF from scratch, and if you’re tricky, you can use AIR’s new PDF capabilities (Warning! You will need the latest version of Acrobat, and only the latest version of Acrobat will work! Too bad if, like me you prefer something lighter, like foxit) to construct what you want to print, save it to a temp file, then bring it up in a PDF control in AIR.

The PDF  control, being essentially Acrobat, takes care of printing, previewing, and even saving a copy to disk if your target audience don’t want to print (wasting paper and all).

A brief introduction to PDF in AIR can be found at http://agknology.com/blog/http:/agknology.com/blog/air/29 

But wait, there’s more!

If you think about it, you can do lots of things in PDF. Now that we can generate them dynamically (sort of, but there’s promises of more to come) some implications  are:

- Freedom from FLV as teh only video format (PDFs can handle WMV)

- E-book applications

- lightweight, customised PDF deployment (as the PDF’s can be generated procedurally using a few lines of (compressible) text/code, rather than the actual PDF itself being stored)

A note on the last thing- Thibault Imbert, the author, has also had a crack at a Zip class for AS3. Interesting…

A sample PDF generation application is as follows:

<?xml version=”1.0″ encoding=”utf-8″?>
< mx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml&#8221; layout=”absolute” applicationComplete=”init()”>
< mx:Script>
< ![CDATA[
import org.alivepdf.fonts.Style;
import org.alivepdf.layout.Layout;
import org.alivepdf.pdf.PDF;
import org.alivepdf.*
import org.alivepdf.saving.Method;
import mx.controls.Alert;
import mx.events.CloseEvent;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;

private var display:HTMLControl;
private    var someText:String = 'Hello World';
private var actualPDF:ByteArray = new ByteArray();
private var pdf:PDF = new PDF();
//    private var page1:org.alivepdf.
private function init():void{

pdf.addPage();
}

private function showClicked(e:Event = null):void{

pdf.setFont('Arial','',10);

pdf.writeText(5, someText);
var size:int = 20;
pdf.setFontSize(size);
pdf.writeText(size, someText);
size= 5;
pdf.setFontSize(size);
pdf.writeText(size, someText);
//    pdf.writeText(size, pdf.setFontSize(10)'Hello');;
//pdf.addMultiCell(100,50,someText,1);
trace('about to save pdf');
actualPDF = pdf.savePDF(Method.LOCAL);
//trace('text size is'+pdf
//pdf.addEventListener(Event.COMPLETE, saveIt);
}

private function saveIt(e:Event = null):void{
var file:File = File.desktopDirectory;
file = file.resolvePath('temp.pdf' );
var stream:FileStream = new FileStream();;
stream.open(file, FileMode.WRITE);
stream.writeBytes( actualPDF);
stream.close();
trace('Saved file');
}

public var stringToDisplay:String = new String();

private var desktopDir:File = File.desktopDirectory;
public function openString(e:Event = null):void{

try {
desktopDir.browseForOpen('Pick a file');
desktopDir.addEventListener(Event.SELECT, openFile);
}catch(error:Error){
Alert.show('There was an error writing a file', "Error", Alert.OK )
}
}

private function openFile(e:Event = null):void{
//trace('Storage/openFile is opening file:'+path)

var file:File = e.target as File;
if (file.exists){
var stream:FileStream = new FileStream()
stream.open(file, FileMode.READ);
someText = stream.readUTFBytes(stream.bytesAvailable);
stream.close()
}else{
Alert.show("There was an error reading a file", "Error");

}

}

]]>

</mx:Script>
< mx:Button x=”111″ y=”105″ label=”make pdf” click=”showClicked()”/>
< mx:Button x=”111″ y=”151″ label=”save it” click=”saveIt();”/>
< mx:Button x=”110″ y=”57″ label=”open text” click=”openString()”/>
< /mx:WindowedApplication>

have fun! and I’ll get back to matters of the flesh, soon enough…

Posted by 1010
반응형

AlivePDF

ActionScript 3 Open-Source PDF Library – 100% client side PDF generation which makes you and your server happy ;)

AlivePDF 0.1.5 [RC] new API’s

AlivePDF 0.1.5 RC is finally there and has a lot of new features. As you may have seen, font embedding is now supported and a lot of new things, as a result a lot of new API's have been introduced. Hopefully the documentation is now updated. To get started with those API's here is a brief introduction :

Font Embedding support :

You can embed TTF or PFM fonts now and use them through your PDF document. To use an embedded font, just use the EmbeddedFont class and pass the TTF stream to the EmbeddedFont class constructor, then pass it to the setFont method which will automatically call the addFont method if needed :

var msg:String = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."

 

var myEmbeddedFont:EmbeddedFont = new EmbeddedFont( new fontStream(), new afmStream(), CodePage.CP1252 );
myPDF.addPage();
myPDF.setFont( myEmbeddedFont, 20 );
myPDF.writeText(12, msg);

As you can see, three parameters are required, the first one is the font stream as a ByteArray, the second one is the AFM file which describes the font, that you can get easily for a font or generate it through a tool like TTF2PT1 or even online here. The third parameter is the codepage to use for your font, which defines the character code mapping between 0 and 255. Note that, only CodePage.1252 is supported for now, but this will be fixed in the final release.

If you want to use standard fonts, known as non-embedded fonts, just use the CoreFont class :

var msg:String = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."

 

var myCoreFont:IFont = new CoreFont ( FontFamily.HELVETICA_BOLD );
myPDF.addPage();
myPDF.setFont( myCoreFont, 20 );
myPDF.writeText(12, msg);

Cell API additions

New Cell API's have been introduced allowing you to force its content to fit in a cell :

var shortText:String = 'This text is short enough.';
var longText:String = 'This text is way too long.';

 

p.addCellFitScale(0, 10,shortText,1,1);
p.addCellFitScale(0, 10,longText,1,1,'',1);

p.addCellFitScaleForce(0,10,shortText,1,1,'',1);
p.addCellFitScaleForce(0,10,longText,1,1,'',1);

p.addCellFitSpace(80,10,shortText,1,1);
p.addCellFitSpace(80,10,longText,1,1,'',1);

p.addCellFitSpaceForce(0,10,shortText,1,1,'',1);
p.addCellFitSpaceForce(0,10,longText,1,1,'',1);

EPS Support :

You can now draw EPS or AI files (8.0 version or below required) into your PDF, by using the addEPSImage method, more infos here : http://alivepdf.bytearray.org/?p=358

A drawSector() API has been added to allow you to create dynamic charts very easily, the following code creates a simple pie chart :

var xCenter:int = 105;
var yCenter:int = 60;
var radius:int = 40;

 

p.lineStyle( new RGBColor ( 0x000000 ), .1 );
p.beginFill( new RGBColor ( 0x0099CC ) );
p.drawSector(xCenter, yCenter, radius, 20, 120);
p.beginFill( new RGBColor ( 0x336699 ) );
p.drawSector(xCenter, yCenter, radius, 120, 250);
p.beginFill( new RGBColor ( 0x6598FF ) );
p.drawSector(xCenter, yCenter, radius, 250, 20);

BeginBitmapFill Drawing API

Just like in Flash Player, a beginBitmapFill API has been added, more infos here : http://alivepdf.bytearray.org/?p=324

Spot Color Support

As requested, Spot color support has been introduced, more infos here : http://alivepdf.bytearray.org/?p=296

Document-level navigation

As requested, document-level navigation has been added, more infos here : http://alivepdf.bytearray.org/?p=279

CSV Grid Export

As requested, the Grid class allows you to export its data as a CSV file: http://alivepdf.bytearray.org/?p=259

So, what's next ? Some final fixes in the UnicodePDF class for other characters like (greek, russian) and a new Invoice API to make invoice generation smooth. I have to finalize the integration of the AlivePDF extension for templates created by Gonzalo. But I may have some interesting news concerning exporting drawn vectors to the PDF, without having to rasterize stuff ;)

Posted by 1010
반응형

출처 : http://flystone.tistory.com/61

SpriteVisualElement (Spark) | Flex Examples 

SpriteVisualElement 는 Flex 의 UIComponent 같은 역활을 한다.
Flex 에 Sprite 객체를 넣을 수 있는 Element 이다.

클래스명에서 보는것과 같이 Elemet 이므로 다른 컴포넌트에 넣을때에는
addChild() 가 아닌 addElement() 로  해야만 정상 작동 한다.

또한 이 엘리멘트를 사용할 때 주의해야 할 것은
아무리 addChild() 로 Sprite 객체든 다른 것들을 넣고 transform 작업을 하여도
실질적으로 이 엘리멘트에 width, height 값은 변하지 않는다.

따라서 직접적으로 width, height 를 변환해 주어야 다른 컴포넌트들과의 위치 정렬및 스크롤러 생성 등의
좌표 관련 설정이 원하는데로 나올 것 이다. 

 

Posted by 1010
반응형

Printing with Flex: Chapter 11 - Enterprise Development with Flex

by Yakov Fain, Anatole Tartakovsky, Victor Rasputnis
Enterprise Development with Flex book cover

This excerpt is from Enterprise Development with Flex. If you want to use Adobe Flex to build production-quality Rich Internet Applications for the enterprise, this groundbreaking book shows you exactly what's required. You'll learn efficient techniques and best practices, and compare several frameworks and tools available for RIA development -- well beyond anything you'll find in Flex tutorials and product documentation. Through many practical examples, the authors impart their considerable experience to help you overcome challenges during your project's life cycle.

buy button

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.
--Bill Gates

In general, the process of printing from web applications works a little differently compared to printing from the desktop. Web applications have good reason for not allowing direct access to a user’s printer: malicious websites could immediately start printing their fliers on your home or corporate printer, offering you anything from pizza delivery to adult entertainment. That’s why you can’t write a program in JavaScript that would automatically detect all available printers and send them print jobs. That’s why the user is forced to manually select the printer via the web browser’s pop-up dialog window.

Existing Flash Player bugs add more issues for Flex developers; for example, the Print dialog might not report all features of the available printer, and setting such parameters as tray selection or paper size might not be possible. To put it simply, you may not have complete control over the user’s printer from an application running in Flash Player. You may need to adjust your reports to standard printer settings.

Note

Adobe had a product called FlashPaper that tried to mitigate these limitations by adding ActionScript 2 objects to a special control with complete access to the printer. In 2008, however, Adobe discontinued FlashPaper, apparently promoting printing PDF documents using Acrobat instead.

The process of printing from Flash Player consists of starting a single-threaded print job and adding dynamically created pages to it (i.e., the data that comes from a database). Unfortunately, Flash Player’s virtual machine AVM2 ActionScript timeout is 15 seconds. Accordingly, for both Flex and AIR, the interval between the following commands shouldn’t be more than 15 seconds:

  • PrintJob.start() and the first PrintJob.addPage()

  • PrintJob.addPage() and the next PrintJob.addPage()

  • PrintJob.addPage() and PrintJob.send()

If, at each of these commands, printing the specified page always completed in 15 seconds or less, your application will be able to print a multipage document, although somewhat slowly. If any of the intervals spans more than 15 seconds, however, your print job will receive a runtime exception, which turns direct printing from Flash Player into an unpleasant experience, if application developers don’t handle exceptions properly. Plus, if the internal thread that started the print job failed, it may be automatically closed and unable to be recovered properly.

Note

You can read more about handling printing errors in the Adobe document “Flash Player and Air tasks and system printing”.

You may think that setTimeout() can help break the 15-second barrier for printing, but it can’t. Printing has to be handled by the same internal AVM2 thread (apparently a bug), and with setTimeout(), you are in fact spawning a new one. The issue with printing long documents is demonstrated in Example 11.1, “PrintTimeout.mxml—an illustration of printing failure”. The PrintJob starts and the method finishPrinting() is called in the same thread and works fine. If you instead comment out the call to finishPrinting() and uncomment the method setTimeout(), this printing job will fail: the addPage() will throw an exception, because it runs in a thread different than PrintJob.

Imagine that a timeout was initiated not by calling the function setTimeout(), but rather by Flash Player during printing of a multipage document because one of the addPage() calls took longer than 15 seconds. In this case, addPage() would be called on a different internal thread than PrintJob.start() and the addPage() operation would fail, even though Flash Player should’ve known how to process a such situation properly.

Example 11.1. PrintTimeout.mxml—an illustration of printing failure

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical">
   <mx:Button label="Print Me" click="printMe()"/>
   <mx:Script>
      <![CDATA[

         private function printMe() :void {
            var pj:PrintJob = new PrintJob();
            pj.start();

   //           setTimeout(function() :void { finishPrinting(pj);}, 1);

                   finishPrinting(pj);
         }

         private function finishPrinting(pj:PrintJob): void {
            pj.addPage(this);
            pj.send();
         }
      ]]>
   </mx:Script>
</mx:WindowedApplication>

Example 11.1, “PrintTimeout.mxml—an illustration of printing failure” just prints itself, addPage(this), but if it had to print, say, a slow-rendered DataGrid with a couple of thousand rows, the chances are high that such a program would time out before the printing job was finished.

There is a bigger problem than the technical restrictions mentioned so far, and it is in the very approach to printing via the PrintJob API. The process of programming reports in ActionScript comes down to creating snapshots of components displayed on the users’ monitors and sending them to the printer. Because screen resolution differs from printer resolution, however, application developers pursing this method need to create separate layouts just for printing, which is time-consuming and challenging.

That’s why you should consider creating and printing your reports as PDF files. Besides, it neatly reinforces this book’s philosophy: minimize the amount of code that business application developers have to write. In this chapter, you’ll learn how to create XDP-enabled Flex components that will allow application developers to generate PDF documents on the client side with minimal coding.

PDF Generation on the Server

PDF generation is supported by Adobe LiveCycle and LCDS, as well as other server-side products. Suppose that you have a Flex or AIR window with a number of UI controls, and you want to create a PDF out of it. One option is to create a snapshot of the Flex component or container using the class mx.graphics.ImageSnapshot and its function captureImage(), which can scale the image to a specific resolution and encode it into a specified image format. You can send an instance of this class via RemoteObject to the server with LCDS installed. LCDS then creates a PDF document (or merges it with a PDF form) and includes the new image received from the client.

The problem with this approach is that the resulting PDF will not be searchable. For instance, if a Flex Panel has text fields, you won’t be able to find the text of these fields in Acrobat Reader if the Panel is embedded as a bitmap.

Such PDFs have limitations on resolution as well (to create a PDF with resolution 300 dpi, you’d need to create a multimegabyte image). Also, printed materials often use different CSS and metrics from the screen ones. You don’t want to print, say, a background gradient that looks fine on the monitor, but bad on paper.

To embed forms into PDF documents, Adobe uses the XDP format. If you purchase an LCDS license, you’ll have the option to use it. You can design forms in Acrobat Designer and export the data from your Flex view, and LCDS will merge the data and the form on the server. On the Java side, LCDS adds several JARs in the lib directory of your web application, which makes the XFAHelper Java class available for your server-side PDF generation.

After generating the PDF, the server-side program can be:

  • Placed as a ByteArray in HTTPSession object

  • Saved as a file on the server for future use

  • Streamed back to the client marked as a MIME type application/pdf

  • Saved in a DBMS field as a binary large object (BLOB)

Depending on the business requirements, the server-side PDF generation might not be feasible. You might have just disconnected the AIR application, or the server software may not have any of the technologies supporting PDF creation installed. If the Flex UI is truly dynamic, that might change the number of displayed components based on some business criteria; developing an additional UI in Acrobat Designer just for printing can in these ways become either impossible or time-consuming. The LCDS Developer Guide describes this process in the document called “Using the PDF Generation Feature”.

Note

Adobe has published an article describing the process of creating PDF documents using templates.

In general, for server-side PDF generation from Adobe Flex applications, you have to do the following:

  • Use Adobe LiveCycle Designer ES, which provides tools for creating interactive forms and personalized documents (see http://www.adobe.com/products/livecycle/designer/). This software comes with Acrobat Professional or can be purchased separately, and is well documented, but it requires someone to create the XDP form and the data model and establish a process of synchronizing the Flex application with the server-side LiveCycle.

  • Initiate the server-side PDF generation from your Flex application seamlessly.

Although this process provides guaranteed quality and predictable results, it also requires the double effort of developing XDP forms for printing and Flex forms for displaying. Besides, LiveCycle Designer is another piece of software that application developers in your organization may not be familiar with.

LCDS generation with merging data and forms produces good printing quality with LCDS. The Flex client sends data as XML to the server along with the name of the form file (template) to be used for merging, as shown in Example 11.2, “Class FormRenderer.as”. In this case, the LCDS layer just needs to process it with the XDPXFAHelper class and return it as a PDF stream to the browser for displaying.

Note

Only commercial licenses of LCDS support PDF generation.

The ActionScript class FormRenderer sends generated XDP to the server and opens a web browser’s window to display the PDF when it arrives from the server.

Example 11.2. Class FormRenderer.as

import flash.net.*;
import flash.utils.ByteArray;

public class FormRenderer {
 public static function openPdf(xdp:String, target:String="_blank"):void{
   var req:URLRequest = new URLRequest("/createPDF.jsp");
   req.method = URLRequestMethod.POST;

   var ba :ByteArray = new ByteArray();;
   ba.writeMultiByte(xdp, "iso-8859-1");
   ba.compress();
   ba.position = 0;
   req.data = ba;
   navigateToURL(req, target);
 }
}

You also need an XDP file with the data and presentation. If you don’t have LiveCycle Designer, you can make the XDP file programmatically, ensuring that it matches the printer’s paper size and corporate stationery. XDP documents are XML objects, which are easily processed in Flex using E4X syntax, for example:

  1. Declare a variable of type XML, and initialize it with the required XDP template deployed on the server. A fragment of the XDP template may look like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <?xfa generator="AdobeLiveCycleDesigner_V8.0" APIVersion="2.5.6290.0"?>
     <xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2007-01-25T10:40:38Z"
      uuid="784f469b-2fd0-4555-a905-6a2d173d0ee1">
    
      <template xmlns="http://www.xfa.org/schema/xfa-template/2.5/">
       <subform name="form1" layout="tb" restoreState="auto" locale="en_US">
        <pageSet>
         <pageArea name="Page1" id="Page1">
          <contentArea x="0.25in" y="0.25in" w="8in" h="10.5in"/>
          <medium stock="letter" short="8.5in" long="11in"/>
          <?templateDesigner expand 1?></pageArea>
          <?templateDesigner expand 1?></pageSet>
    
          <subform w="8in" h="10.5in" name="YourPageAttachedHere"/>
          <proto/>
          <desc>
            <text name="version">8.0.1291.1.339988.308172</text>
          </desc>
       </subform>
      </template>
  2. Select a Flex UI container or component that you are going to print (a Panel, a DataGrid, and so on).

  3. Query the object from step 2, get its XDP attributes and children, and create the XML preparing this object for printing. Attach the XML to the template as a page.

Because original Flex components don’t know how to represent themselves in the XDP format, you’ll need to teach them to do so. This becomes the next task in enhancing Flex components.

For example, each UI component can implement some interface (e.g., IXdpObject with the only getter, xmlContent()) that allows it to return its own XDP content or, in the case of containers, to traverse the list of its child components for their XDP content. For example, a new panel component (PanelXdp) may have the following structure:

public class PanelXdp extends Panel implements IXdpObject{
    public function get xmlContent():Object{
       // The code to return representation of the panel
       // in the XDP format goes here
    }
}

Repeat the process of attaching XML to the XDP template using E4X until all print pages are ready. This method of printing from Flex requires less effort for reporting and creation of dynamic layouts. It might also provide better printing quality and searchability within the printed document.

Example 11.3, “Render.jsp” is the server-side part written as a Java ServerSide Page. It uncompresses the XDP stream received from the client, creates the PDF using XDPXFAHelper, turns it into an array of bytes, and sends it back to the client as the MIME type "application/pdf".

Example 11.3. Render.jsp

<%@ page language="java"
   import="java.io.*,
   java.util.*,
   javax.xml.parsers.*,
   org.w3c.dom.Document,
   flex.messaging.*,
   flex.acrobat.pdf.XDPXFAHelper,
   flex.messaging.util.UUIDUtils,
   org.w3c.dom.Document
   "
%><%!
private static void _log(Object o){
   System.out.println(""+o);
}
private String getParam(HttpServletRequest request, String name, String defVal)
throws Exception{
       String val = request.getParameter(name);
     return (val!=null && val.length()>0)?val:defVal;
}
private String getParam(HttpServletRequest request, String name) throws Exception{
   return getParam(request, name, null);
}
private void processRenderRequest(HttpServletRequest request,
                        HttpServletResponse response) throws Exception{

   String data      = getParam(request, "document");
   String template = getParam(request, "template"); // Security hole, check path
   _log("template="+template);
   _log("data="+data);
   template = FlexContext.getServletContext().getRealPath(template);
   _log("template real="+template);

       // You must have LCDS license to use XDPXFAHelper
   XDPXFAHelper helper = new XDPXFAHelper();
   helper.open(template);
   // Import XFA dataset
   if( data!=null ){
      _log("data.length="+data.length());
      ByteArrayInputStream bais = new
                        ByteArrayInputStream(data.getBytes("UTF-8"));
      DocumentBuilderFactory builderFactory =
                                    DocumentBuilderFactory.newInstance();
      DocumentBuilder builder =
                                    builderFactory.newDocumentBuilder();
      Document dataset = builder.parse(bais);
      helper.importDataset(dataset);
   } else
      _log("data="+null);

   byte[] content = helper.saveToByteArray();
   _log("content="+content);
   helper.close();
   ServletOutputStream out3 = response.getOutputStream();
   response.setContentType("application/pdf");
   response.setContentLength(content.length);
   out3.write(content);
}
%><%
_log("");
_log("--------------------------------------------");
_log("render.jsp");
processRenderRequest(request, response);
%>

Note

The WebORB PDF Generator from Midnight Coders allows you to either create XML printing templates on the server or generate them in Flex clients. To use this solution, you have to install the WebORB Server. For more details, visit http://www.themidnightcoders.com/products/pdf-generator/overview.html.

Now we’ll take a look at how to generate a PDF on the Flex side.

PDF Generation on the Client

AlivePDF is an open source library for generating PDFs in ActionScript on the client side. It’s offered under the MIT license at http://alivepdf.org; download AlivePDF.swc and link it to your Flex Builder project. One of the classes included in AlivePDF.swc is called PDF.

Note

Unless you are developing an AIR application or deploying it for Flash Player 10 (see the next note), even client-generated PDF content has to be sent to a server that will just bounce it back (see Example 11.20, “PDFServlet.java”) to have the web browser open the Acrobat Reader plug-in.

Basic Printing with AlivePDF

The process of generating PDFs in AlivePDF starts with instantiation of the PDF class, specifying the print orientation, units of measurement, and the paper size. Then you create and add pages to the instance of the PDF object, and finally you call the function savePdf() to turn these pages into a ByteArray and save them to the PDF file on your filesystem if you use Adobe AIR. If this is a web application written in Flex, the same savePdf() function sends the ByteArray to the server with the deployed script create.php (supplied by alivepdf.org), which will return this array of bytes to your web browser as a PDF document.

Note

Starting from Flash Player 10, the FileReference class allows you to save files locally. Its function save() opens the pop-up window, allowing the user to specify the filename for saving the data. In our example, this eliminates the need for a round trip to the server that does nothing but bounce this array of bytes. Keep in mind, though, that after saving the PDF this way, the user will need to complete an extra step to open the file with Acrobat Reader or any other program.

Example 11.4, “Basic printing with AlivePDF: test1.mxml” shows the process of preparing and saving a PDF file with AlivePDF.

Example 11.4. Basic printing with AlivePDF: test1.mxml

<?xml version="1.0" encoding="utf-8"?>
    <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
   layout="vertical" horizontalAlign="center" verticalAlign="middle">

   <mx:Button label="Hello World!!!" click="hw()"/>

 <mx:Script><![CDATA[

   import org.alivepdf.fonts.Style;
   import org.alivepdf.fonts.FontFamily;
    import org.alivepdf.saving.Method;
    import org.alivepdf.layout.Size;
    import org.alivepdf.layout.Unit;
        import org.alivepdf.layout.Orientation;
    import org.alivepdf.pdf.PDF;

  private function hw():void{

   var p:PDF = new PDF(Orientation.PORTRAIT, Unit.MM, Size.A4);
   p.addPage();
   p.setFont(FontFamily.ARIAL, Style.NORMAL, 12);
   p.addText("10x10", 10, 10);
   p.addText("100x100", 100, 100);
   p.addMultiCell(50, 8, "Hello World2");

   savePdf(p, "hw.pdf");
 }

 private function savePdf(p:PDF, fileName:String):void{

   var ba:ByteArray = p.save(Method.LOCAL);
   var fs:FileStream = new FileStream();
   var f:File = File.desktopDirectory.resolvePath(fileName);
   fs.open(f, FileMode.WRITE);

   try{
      fs.writeBytes(ba);
   } catch (e:*){}

     fs.close();
 }
]]></mx:Script>

</mx:WindowedApplication>

After you click the button Hello World!!! (see the example code), a file called hw.pdf is created in the AIR desktop directory (see Chapter 9, Working with Adobe AIR for details). For example, Figure 11.1, “Sample output of the AlivePDF program” shows our hw.pdf file, which was saved in the directory C:\Documents and Settings\Administrator\Desktop.

Figure 11.1. Sample output of the AlivePDF program

Sample output of the AlivePDF program

The goal here was to give you a taste of the process of preparing the document with AlivePDF. To investigate the complete set of AlivePDF’s APIs, visit http://AlivePDF.org.

AlivePDF does a good job of creating objects and assembling them into pages, which are then converted into a PDF format. But it still requires you to prepare (in addition to the screen version) a second copy of the UI to be printed. It’s not what-you-see-is-what-you-get (WYSIWYG) programming. This process requires manual allocation and measurements of each object in the PDF-to-be.

Note

The blog http://alivepdf.bytearray.org is yet another good source of up-to-date information regarding AlivePDF.

Enhancing AlivePDF

What can be done to improve this process? We still want to use AlivePDF’s printing engine, but we don’t want to manually write the code specifying styles and measurements as we did in Example 11.4, “Basic printing with AlivePDF: test1.mxml”. In this section, you’ll see how to create components and containers that are smart enough to present themselves as PDF or XDP objects.

Note

All examples from this section are located in the Flex Builder project called clientPdfAir (which comes with this chapter; see the Preface for information on obtaining the sample code) and are self-contained AIR applications. alivePDF.swc has to be present in the build path of the project.

The program test2.mxml in Example 11.5, “Printing with AlivePDF from an AIR application” illustrates Flex-to-PDF-object conversion, a big difference compared to test1.mxml. It uses the ActionScript class AlivePdfPrinter, which is included with the code samples of this chapter. Its addObject() method converts a given Flex object to corresponding PDF snippets, one at a time. You don’t need to worry about the sizes, locations, fonts, or styles of these objects. This is WYSIWYG. Flash Player is a rendering engine here.

Example 11.5. Printing with AlivePDF from an AIR application

<?xml version="1.0" encoding="utf-8"?>
    <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
     xmlns:printer="com.farata.printing.pdf.client.*"
   layout="vertical" >
   <mx:Style source="main.css"/>
   <mx:Canvas width="100%" height="100%" backgroundColor="white">
     <mx:Label id="lbl1" text="Hello" x="10" y="10"/>
     <mx:Label id="lbl2" text="World" x="50" y="30" fontWeight="bold"/>
          <mx:Label id="lbl3" text="And" x="150" y="60" fontStyle="italic"
                                                        enabled="false"/>
     <mx:Label id="lbl4" text="Happy" x="70" y="90" fontSize="16"
                                         textDecoration="underline"/>
         <mx:Label id="lbl5" text="New Year" x="50" y="140" fontSize="24"
                                        fontWeight="bold" color="green"/>
     <mx:Button id="btn1" label="Button1" x="70" y="240"/>
   </mx:Canvas>

 <mx:ApplicationControlBar width="100%">
   <mx:Label text="File name:"/>
   <mx:TextInput id="txtFileName" text="hw2.pdf"/>
   <mx:Button label="Save PDF" click="printPdf()"/>
     </mx:ApplicationControlBar>

   <printer:AlivePdfPrinter id="prn" printComplete="viewPdf()"/>
<mx:Script><![CDATA[

  import flash.net.URLRequest;

  private var file:File;

  private function printPDF():void{

   prn.addObject(lbl1);
   prn.addObject(lbl2);
   prn.addObject(lbl3);
   prn.addObject(lbl4);
   prn.addObject(lbl5);
   prn.addObject(btn1);

   file = prn.printToFile (txtFileName.text);
 }

 private function viewPdf():void{
   var req:URLRequest = new URLRequest(file.url);
   navigateToURL(req, "_blank");
}

]]></mx:Script>

</mx:WindowedApplication>

The code in Example 11.5, “Printing with AlivePDF from an AIR application” produces the screen as it’s shown in the AIR runtime, on the left in Figure 11.2, “The results of printing with enhanced AlivePDF”. On the right side, you can see the hw2.swf file produced by this program and shown in Adobe Acrobat. The fonts in the Acrobat Reader image look smaller because of the small zoom percentage.

Figure 11.2. The results of printing with enhanced AlivePDF

The results of printing with enhanced AlivePDF

The listings that follow do not include the entire source code of the class AlivePdfPrinter; that comes with the source code of this chapter’s samples. The idea is to have a set of components that can expose their information in a form suitable for printing. The method AlivePdfPrinter.addObject(o) calls locateExtension(), which instantiates the appropriate object that can present itself in a form suitable for AlivePDF (Example 11.6, “The method AlivePdfPrinter.locateExtension()”).

Example 11.6. The method AlivePdfPrinter.locateExtension()

private static function locateExtension(o:*):IPdfPrinterExtension{
  if( o is Label )
   return new LabelPdfExtension(/*o*/);
  if( o is PdfPrintDataGrid )
   return new PdfPrintDataGridPdfExtension(/*o*/);
  if( o is DataGrid )
   return new DataGridPdfExtension(/*o*/);
  if( o is Container )
   return new ContainerPdfExtension(/*o*/);
  if( o is UIComponent )
   return new UIComponentPdfExtension(/*o*/);
 return null;
}

After identifying the type of the object to be printed, the object exposes its font and style information that’s being passed to the AlivePDF’s PDF object for printing. Example 11.7, “The addObject() method in the LabelPdfExtension class” shows the function addObject() from the ActionScript class com.farata.printing.pdf.client.extensions.LabelPdfExtension.

Example 11.7. The addObject() method in the LabelPdfExtension class

public function addObject(o:*, printer:IAlivePdfPrinter):void{
 var pdf:PDF = printer.pdf;
   var c:Label = Label(o);
   if( c==null ) return;
   if( !c.visible )
      return;

   var fontFamily:String = c.getStyle("fontFamily");
   if( fontFamily==null )
      fontFamily = FontFamily.ARIAL;

   var style:String = "";

   if( c.getStyle("fontWeight")=="bold" )
       style += "B";

   if( c.getStyle("fontStyle")=="italic" )
      style += "I";
   if( c.getStyle("textDecoration")=="underline" )
        style += "U";

   var size:int = c.getStyle("fontSize");
   var color:Color = new RGBColor(c.getStyle(c.enabled?"color":"disabledColor"));

   allocateSpace(c, printer);

   pdf.textStyle(color/*, alpha*/);
   pdf.setFont(fontFamily, style, pxToPt(size));

   var ptText:PPoint = mapPoint(c, printer);
   ptText.y.px += c.height;

   pdf.addText(c.text, ptText.x.pt, ptText.y.pt);

 }

Example 11.7, “The addObject() method in the LabelPdfExtension class”’s code gets the styles, font, and text from the Flex Label object and initializes appropriate properties of the PDF object per the requirements of the AlivePDF library.

The sample code of this chapter as well as the clear.swc library has several similar extensions for a number of Flex components (see com.farata.printing.swc), and you can keep adding more objects to your own business framework of PDF-ready components.

These extensions are not subclasses of corresponding Flex components, but rather utility classes that know how to present the content of components to AlivePDF.

Note

While writing the method addObjects() for your components, keep in mind that measurements in Flash Player are in pixels and you may need to convert them into other units required by the AlivePDF API.

If you’d like to see what’s inside the generated hw2.pdf file, just open it with any text editor; you’ll see something like Example 11.8, “A fragment of the h2.pdf content” (which is just a fragment of the file).

Example 11.8. A fragment of the h2.pdf content

%PDF-1.4
1 0 obj
<</Type /Pages
/Kids [3 0 R
]
/Count 1
>>
endobj
3 0 obj
<</Type /Page
/Parent 1 0 R
/MediaBox [0 0 595.28 841.89]
/Resources 2 0 R
/Rotate 0
/Dur 3
/Contents 4 0 R>>
endobj
4 0 obj
<</Length 780>>
stream
2 J
0.57 w
0 Tr
/GS0 gs
0 Tw 0 Tc 100 Tz 0 TL
BT /F1 7.00 Tf ET
q 0.043137254901960784 0.2 0.23529411764705882 rg BT 25.50 802.14 Td (Hello) Tj ET
Q
0 Tr
/GS1 gs
0 Tw 0 Tc 100 Tz 0 TL
BT /F2 7.00 Tf ET
q 0.043137254901960784 0.2 0.23529411764705882 rg BT 55.50 787.89 Td (World) Tj ET
Q
0 Tr
/GS2 gs
0 Tw 0 Tc 100 Tz 0 TL
BT /F3 7.00 Tf ET
q 0.6666666666666666 0.7019607843137254 0.7019607843137254 rg BT 130.50 764.64 Td
(And) Tj ET Q
0 Tr
/GS3 gs
0 Tw 0 Tc 100 Tz 0 TL
BT /F1 12.00 Tf ET
q 0.043137254901960784 0.2 0.23529411764705882 rg BT 70.50 738.39 Td (Happy) Tj ET
70.50 737.19 34.68 0.60 re f Q
0 Tr
/GS4 gs
0 Tw 0 Tc 100 Tz 0 TL
BT /F2 18.00 Tf ET
q 0 0.5019607843137255 0 rg BT 55.50 692.64 Td (New Year) Tj ET Q
/GS5 gs
q 47.25 0 0 16.50 52.50 645.39 cm
/I1 Do Q
endstream
endobj
5 0 obj
<</Type /ExtGState
/SA true
/CA 1
/n 5
/BM /Normal
/ca 1
>>
endobj
...
15 0 obj
<<
/Producer (Alive PDF 0.1.4.6)
/CreationDate (D:200905152226)
>>
endobj
16 0 obj
<<
/Type /Catalog
/Pages 1 0 R
/OpenAction [3 0 R /FitH null]
/PageLayout /SinglePage
>>
endobj
xref
...
trailer
<<
/Size 17
/Root 16 0 R
/Info 15 0 R
>>
startxref
2467
%%EOF

Note

After this chapter was written, a new open source library called purePDF became available. It's an ActionScript port of a popular Java library called iText. You can download purePDF at http://code.google.com/p/purepdf/.

Printing Flex Containers

All these extensions for Flex controls are great, but there is another issue to tackle: Flex views often use containers. For example, you need to be able to generate a PDF for a DataGrid in two formats. This object should be similar to mx.printing.PrintDataGrid, but it should support PDF printing rather than working with PrintJob. It should support pagination, headers, and footers; this is a must for printing multipage documents.

Or imagine a TabNavigator container from which you need to print the content of each tab as a separate multipage PDF. The goal is to have a container that can iterate its children and tell each of them, “Hey, kiddo, print yourself.” When this mission is accomplished, just implement the same behavior to allow containers (and components) to expose themselves in the XDP format, too.

Sample extensions for the most complex Flex components, such as DataGrid, DataGridItemRenderer, and Canvas, are supplied as code samples for this chapter. Use them as a guide for the creation of your own printing extensions.

For example, the application test4.mxml includes the PdfPrintDataGrid component from clear.swc and outputs the data grid to the file hw4.pdf, as shown in Figure 11.3, “Running test4.mxml (left) generates PDF hw4 (right)” and Example 11.9, “An AIR application to print a data grid to a PDF file”.

Figure 11.3. Running test4.mxml (left) generates PDF hw4 (right)

Running test4.mxml (left) generates PDF hw4 (right)

Example 11.9. An AIR application to print a data grid to a PDF file

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
  xmlns:printer="com.farata.printing.pdf.client.*"
  layout="vertical"
  creationComplete="onCreationComplete(event)" >

   <mx:DataGrid id="dg" x="0" y="0"
   width="100%" height="100%" >

    <mx:columns>
      <mx:DataGridColumn dataField="name" headerText="Name"/>
      <mx:DataGridColumn dataField="phone" headerText="Phone"/>
      <mx:DataGridColumn dataField="email" headerText="E-mail"/>
    </mx:columns>
   </mx:DataGrid>

   <mx:ApplicationControlBar width="100%">
    <mx:Label text="File name:"/>
    <mx:TextInput id="txtFileName" text="hw4.pdf"/>
     <mx:Button label="Save PDF" click="doSavePdf()"/>
   </mx:ApplicationControlBar>

   <printer:AlivePdfPrinter id="prn" printComplete="viewPdf()"/>

<mx:Script><![CDATA[

import flash.net.URLRequest;
private var file:File;

private function onCreationComplete(evt:*):void{

   var array:Array = [];

   for(var i:int=1; i<=30; i++){
       var obj:Object = new Object();
      obj.name   = "User " +i;
      obj.phone  = "555-219-227"+i;
      obj.email  = "user"+i+"@hello.world";
      obj.active = (i % 2 ) == 1;
      array.push(obj);

   }
   dg.dataProvider = array;
}
private function printPdf():void{

   prn.addObject(dg);
   file = prn.printToFile(txtFileName.text);
}

private function viewPdf():void{
   var req:URLRequest = new URLRequest(file.url);
   navigateToURL(req, "_blank");
}

]]></mx:Script>
</mx:WindowedApplication>

The line:

prn.addObject(dg);

results in invoking the code from Example 11.6, “The method AlivePdfPrinter.locateExtension()”, and the DataGridPdfExtension class shown in Example 11.10, “Class DataGridPdfExtension” is engaged.

Example 11.10. Class DataGridPdfExtension

package com.farata.printing.pdf.client.extensions{

   import com.farata.printing.PdfPrintDataGrid;

   import com.farata.printing.pdf.client.IAlivePdfPrinter;
   import mx.controls.DataGrid;

   public class DataGridPdfExtension extends BasePdfExtension{

    override public function addObject(o:*, printer:IAlivePdfPrinter):void{

     var c:DataGrid = o as DataGrid;
      var p:PdfPrintDataGrid = new PdfPrintDataGrid();
      p.x = c.x;
       p.y = c.y;
     p.width = c.width;
     p.height = c.height;
     p.columns = c.columns;
     p.dataProvider = c.dataProvider;

      c.parent.addChild(p);
     printer.addObject(p);
     c.parent.removeChild(p);
   }

 }
}

If in Example 11.6, “The method AlivePdfPrinter.locateExtension()” all components were located inside, say, Canvas, and the printing extensions for this container were ready, this code sample would become even shorter—something like this:

prn.addObject(myCanvas);

The myCanvas component would’ve taken care of its kids.

The good news is that you don’t have to write printing extensions to all components. The code in Example 11.6, “The method AlivePdfPrinter.locateExtension()” checks to see whether the component is an instance of Label, DataGrid, or Container.

Part of the sample code in the test3.mxml application has a canvas:

<mx:Canvas id="canvas" width="100%" height="100%"
      backgroundColor="white">

   <mx:Label id="lbl1" text="Hello" x="100" y="10"/>
    <mx:Label id="lbl2" text="World" x="50" y="30" fontWeight="bold"/>
    <mx:Label id="lbl3" text="And" x="150" y="60" fontStyle="italic"
              enabled="false"/>
    <mx:Label id="lbl4" text="Happy" x="90" y="90" fontSize="16"
                                      textDecoration="underline"/>
   <mx:Label id="lbl5" text="New Year" x="80" y="140" fontSize="24"
     fontWeight="bold" color="green"/>
   <mx:Button id="btn1" label="Button1" x="1" y="1"/>
    <mx:Button id="btn2" label="Button2" x="10" y="100"/>

    <mx:HBox x="250" y="130" borderThickness="3" borderColor="blue"
       borderStyle="solid"   backgroundColor="yellow">
      <mx:Label text="Inside HBox" color="gray"/>
   </mx:HBox>
</mx:Canvas>

The code to print this Canvas is pretty simple: just pass a reference to the class AlivePdfPrinter, and it’ll figure out how to print its child components:

private function printPdf():void{
   prn.addObject(canvas);
   file = prn.printToFile(txtFileName.text);
}

The function addObject() tries to locate an extension class for Canvas as shown in Example 11.6, “The method AlivePdfPrinter.locateExtension()”, and will use the ContainerPdfExtension, because Canvas is a Container. Should you want to provide some functionality specific to Canvas, you need to create CanvasPdfExtension and modify the code in Example 11.6, “The method AlivePdfPrinter.locateExtension()” accordingly.

A fragment of ContainerPdfExtension is shown in Example 11.11, “The main part of ContainerPdfExtension”.

Example 11.11. The main part of ContainerPdfExtension

package com.farata.printing.pdf.client.extensions{
 public class ContainerPdfExtension extends BasePdfExtension{

   private static var s_offsetLock:int = 0;

   override public function addObject(o:*, printer:IAlivePdfPrinter):void{

    s_offsetLock++;
    var c:Container = Container(o);
    setOffset(c, printer);
    allocateSpace(c, printer);
    drawBackgroundAndBorder(c, printer);

     var len:int = c.numChildren;
    for(var i:int=0; i<len; i++){
      var ch:DisplayObject = c.getChildAt(i);
      printer.addObject(ch);
    }

    s_offsetLock--;
    }

   private  function setOffset(o:Container, printer:IAlivePdfPrinter):void{
     if( s_offsetLock==1 ) {
      var ptLocal:Point = new Point(o.x, o.y);
        var ptGlobal:Point = o.parent.localToGlobal(ptLocal);
        printer.lastOffset.x.px = ptGlobal.x;
        printer.lastOffset.y.px = ptGlobal.y;
     }
    }
 }

}

All other components that the code in Example 11.6, “The method AlivePdfPrinter.locateExtension()” won’t recognize will be printed as prescribed in the UIComponentPdfExtension as a snapshot of an image (Example 11.12, “Printing a Flex object as an image”).

Example 11.12. Printing a Flex object as an image

public function addObject(o:*, printer:IAlivePdfPrinter):void{

   if( !o.visible ) return;
   var c:UIComponent = o;
   var pdf:PDF = printer.pdf;
   var rc:PRectangle = new PRectangle();
   rc.left.px = c.x;

   rc.top.px = c.y;
   rc.right.px = rc.left.px+c.width;
   rc.bottom.px = rc.top.px+c.height;

   printer.allocateSpace(rc);
   pdf.addImage(c, rc.left.pt, rc.top.pt, rc.right.pt-rc.left.pt,
   rc.bottom.pt-rc.top.pt);
}

Of course, it’s better not to use a bitmap but instead a PDF representation specific to a component, which will allow Acrobat Reader to recognize its text content and generate searchable documents rather than bitmaps.

Extending Flex Components for PDF Generation in XDP Format

In this section, you’ll learn how to enhance standard Flex UI components so that they can properly present themselves for rendering as PDF-friendly objects in the XML-based XDP format.

The ActionScript code snippet in Example 11.13, “Representing a CheckBox as an XDP checkButton” shows how you can present a checkbox as an element of the PDF form in XDP format (in XDP, a checkbox is called checkButton).

We’ll introduce a new interface, IXdpObject, and each of our enhanced UI components will implement it to return properly prepared XML to represent itself. This will allow you to turn the entire Flex view into a searchable PDF.

Example 11.13, “Representing a CheckBox as an XDP checkButton” is an example of implementing the getter xdpContent() defined in the IXdpObject interface to produce a CheckBox in the XDP format.

Example 11.13. Representing a CheckBox as an XDP checkButton

// IXdpObject interface implementation
public  function get xdpContent():Object {
 var o:XML =
      <field  x={convert(x)} w={convert(width)} h={convert(height)}>
   <ui>
     <checkButton  allowNeutral="1">
       <border>
         <edge stroke="lowered"/>
         <fill/>
       </border>
      </checkButton>
   </ui>
   <value>
     <text>{value}</text>
   </value>
   <para vAlign="middle" hAlign="center"/>

   <items>
     <text>{onValue}</text>
     <text>{offValue}</text>
     <text></text>
   </items>
   <caption placement="bottom"/>
   </field>;

 return o;
}

private function convert(value:Number) : String {
   return  XdpUtil.px2pt(value) + "pt";
}

This code snippet includes a getter, xdpContent, that returns the representation of our CheckBox in XDP format. It uses a helper function, convert(), to convert the value from pixels to points.

Note

Note that this code uses binding to insert the onValue and offValue variables that were introduced in Chapter , Building an Enterprise Framework in Example 55, “CheckBox with value and text properties”.

To generate a PDF for a particular Flex view, you need to loop through its children (every UI control of each container) and get each one’s xdpContent. If it’s not null, add its value (XDP) to the output file. If it does not have xdpConent, just get an image snapshot of this child and add it to the output file.

At the end of this process, you’ll get a mix of images and XDP content. If this is a Flex application, send this content to the server-side Java Servlet, which will sandwich it between the PDF header and footer. Voilà! Your PDF file is ready.

Obsessed with the mantra “Developers must write less code,” we at Farata have already created a number of classes in the package com.farata.printing that allows Flex components to expose themselves in a form of XDP.

The sample application shown in Example 11.14, “Saving data in XDP format: test_xdp2.mxml” is a rewrite of Example 11.5, “Printing with AlivePDF from an AIR application”. It’ll produce the same output as in Figure 11.2, “The results of printing with enhanced AlivePDF”, but this time the document will be encoded in the XDP format.

Example 11.14. Saving data in XDP format: test_xdp2.mxml

<?xml version="1.0" encoding="utf-8"?>

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
 xmlns:local="*"
 xmlns:printer="com.farata.printing.pdf.client.*" layout="vertical">

   <mx:Style source="main.css"/>
   <mx:Canvas id="canvas" width="100%" height="100%"
      backgroundColor="white">
      <mx:Label id="lbl1" text="Hello" x="10" y="10"/>
      <mx:Label id="lbl2" text="World" x="50" y="30"
         fontWeight="bold"/>
           <mx:Label id="lbl3" text="And" x="150" y="60"
          fontStyle="italic" enabled="false"/>

      <mx:Label id="lbl4" text="Happy" x="70" y="90" fontSize="16"
              textDecoration="underline"/>
      <mx:Label id="lbl5" text="New Year" x="50" y="140" fontSize="24"
              fontWeight="bold" color="green"/>
      <mx:Button id="btn1" label="Button1" x="70" y="240"/>
   </mx:Canvas>

    <mx:ApplicationControlBar width="100%">
    <mx:Label text="File name:"/>
     <mx:TextInput id="txtFileName" text="hw2.pdf"/>
    <mx:Button label="Save PDF" click="savePdf()"/>
   </mx:ApplicationControlBar>

   <mx:Script>
      <![CDATA[
      import com.farata.printing.PrintOptions;
        import com.farata.printing.pdf.xdp.XdpDocument;
        import com.farata.printing.pdf.buffered.PDFHelper;

     private function savePdf():void{
      saveToFile(txtFileName.text, createXdpContent());
      }

     private function createXdpContent ():ByteArray{

      var xdpDocument:XdpDocument=new XdpDocument();
      xdpDocument.init(new PrintOptions());
      var pdf:PDFHelper=new PDFHelper(xdpDocument);

      pdf.createPDFPrologue();
      pdf.createPage(canvas, PDFHelper.TYPE_PAGE);
      pdf.createPDFEpilogue();

        return pdf.pdfContent;
   }

   private function saveToFile (file:String, ba:ByteArray):void{

      var fs:FileStream=new FileStream();
      var f:File=File.desktopDirectory.resolvePath(file);
      fs.open(f, FileMode.WRITE);

      try {
        fs.writeBytes(ba);
      } catch(e:*){
             // Process I/O errors here
       }
        fs.close();
   }

      ]]>
   </mx:Script>
</mx:WindowedApplication>

When you open the generated file h2.pdf in a text editor, notice that it looks different than the file shown in Example 11.8, “A fragment of the h2.pdf content”. The small PDF header and the trailer are there, but the main content of this file is in XDP format, as shown in Example 11.15, “A fragment of the h2.pdf content in XDP format”.

Example 11.15. A fragment of the h2.pdf content in XDP format

 %PDF-1.7
1 0 obj
<</Type /Catalog /StructTreeRoot 9 0 R /MarkInfo <</Marked true>> /Pages 15 0 R
/AcroForm 16 0 R /NeedsRendering true>>
endobj
2 0 obj
<</Type /Page /MediaBox [0 0 612 792] /Resources 5 0 R /Contents 4 0 R
/StructParent 0 /StructParents 0 /Parent 15 0 R>>
endobj
4 0 obj
<</Length 298>>
stream
BT
/Content <</MCID 0>> BDC
0.0 0.0 0.0 rg
/RelativeColorimetric ri
/T1_0 1.0 Tf
10.0 0.0 0.0 10.0 72.0 720.0 Tm
(Warning: This form is not supported at all with the current version of Acrobat or
Adobe Reader.) Tj
0.0 -1.8 Td
(Upgrade to the latest version for full support.) Tj
0.0 -1.8 Td
EMC
ET
endstream
endobj
5 0 obj
<</Font 8 0 R /ProcSet [/PDF /Text]>>
endobj
6 0 obj
<</Type /Encoding /BaseEncoding /WinAnsiEncoding>>
endobj
7 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding 6 0 R>>
endobj
8 0 obj
<</T1_0 7 0 R>>
endobj
9 0 obj
<</Type /StructTreeRoot /K 10 0 R /ParentTree 13 0 R /ParentTreeNextKey 1 /RoleMap
14 0 R>>
endobj
10 0 obj
<</S /Document /P 9 0 R /K 11 0 R>>
endobj
11 0 obj
<</S /Div /P 10 0 R /K 12 0 R>>
endobj
12 0 obj
<</S /P /P 11 0 R /Pg 2 0 R /K 0>>
endobj
13 0 obj
<</Nums [0 [12 0 R]]>>
endobj
14 0 obj
<</Field /Div /Subform /Sect /Page /Part /Draw /Div>>
endobj
15 0 obj
<</Type /Pages /Kids [2 0 R] /Count 1>>
endobj
16 0 obj
<</Fields [] /XFA 17 0 R>>
endobj
17 0 obj
<< /Length 18 0 R >>
stream
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
  <template xmlns="http://www.xfa.org/schema/xfa-template/2.5/">
    <subform name="doc1" layout="tb" restoreState="auto" locale="en_US">
      <proto/>
      <desc>
        <text name="version">8.0.1291.1.339988.308172</text>
      </desc>
      <pageSet>
        <pageArea name="Page1" id="Page1">
          <contentArea x="8.47mm" y="8.47mm" w="262.43mm" h="198.94mm"/>
          <medium stock="custom" short="215.87mm" long="279.37mm"
                                              orientation="landscape"/>
        </pageArea>
      </pageSet>
    <subform layout="tb" name="Subform1">
  <subform name="Container1" x="6.35mm" y="6.35mm" w="119.58mm" h="70.9mm">
    <draw x="7.5pt" y="7.5pt" w="22.5pt" h="14.25pt">
      <ui>
        <textEdit hScrollPolicy="off" multiLine="0" vScrollPolicy="off"/>
      </ui>
      <value>
        <text>Hello</text>
      </value>
      <para hAlign="left"/>
      <font typeface="Arial" size="7.5pt" weight="normal" posture="normal"
                   underline="0">
        <fill>
          <color value="11,51,60"/>
        </fill>
      </font>
      <border>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
      </border>
    </draw>
    <draw x="37.5pt" y="22.5pt" w="24.75pt" h="13.5pt">
      <ui>
        <textEdit hScrollPolicy="off" multiLine="0" vScrollPolicy="off"/>
      </ui>
      <value>
        <text>World</text>
      </value>
      <para hAlign="left"/>
      <font typeface="Arial" size="7.5pt" weight="bold" posture="normal"
                                                          underline="0">
        <fill>
          <color value="11,51,60"/>
        </fill>
      </font>
      <border>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
      </border>
    </draw>
    <draw x="112.5pt" y="45pt" w="18pt" h="14.25pt">
      <ui>
        <textEdit hScrollPolicy="off" multiLine="0" vScrollPolicy="off"/>
      </ui>
      <value>
        <text>And</text>
      </value>
      <para hAlign="left"/>
      <font typeface="Arial" size="7.5pt" weight="normal" posture="italic"
                                                 underline="0">
        <fill>
          <color value="11,51,60"/>
        </fill>
      </font>
      <border>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
      </border>
    </draw>
    <draw x="52.5pt" y="67.5pt" w="37.5pt" h="18pt">
      <ui>
        <textEdit hScrollPolicy="off" multiLine="0" vScrollPolicy="off"/>
      </ui>
      <value>
        <text>Happy</text>
      </value>
      <para hAlign="left"/>
      <font typeface="Arial" size="12pt" weight="normal" posture="normal"
                                              underline="1">
        <fill>
          <color value="11,51,60"/>
        </fill>
      </font>
      <border>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
      </border>
    </draw>
    <draw x="37.5pt" y="105pt" w="83.25pt" h="26.25pt">
      <ui>
        <textEdit hScrollPolicy="off" multiLine="0" vScrollPolicy="off"/>
      </ui>
      <value>
        <text>New Year</text>
      </value>
      <para hAlign="left"/>
      <font typeface="Arial" size="18pt" weight="bold" posture="normal"
underline="0">
        <fill>
          <color value="0,128,0"/>
        </fill>
      </font>
      <border>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
      </border>
    </draw>
    <draw x="18.52mm" y="63.49mm" w="16.67mm" h="5.82mm">
      <ui>
        <imageEdit/>
      </ui>
      <value>
        <image
contentType="image/png">iVBORw0KGgoAAAANSUhEUgAAAD8AAAAWCAYAAAB3/EQhAAABqElE
QVR42uWYTUsCURSGz76f07bfEtS2UkioqAgJKaMPERISQkJCqIggF2VItAsRi/5AhS11/BpH8YuT
rysb7jj35m7uhQfuOXPmnPvALGaGeLgaDZNz+cKIh6esZ4Ff4f1j5ItFvV6Ps88v/Pn1zeWywV5e
8IMnfOFNr7k85wtvPBgMtAG+8Kbr2zvu9/vaAW9KXd2MHgHdgDclL1Pc7Xa1A96UuEhyp9PRDnhT
/DzB7XZbmZnZuT/I1Iv20zBNH3hT7CzOlmUpg8GT4kn1brWy86fpA2+Knsa42Wwqg8GiWJR3QlTj
dJ/MLBXgTceRKJumqYz9cON5e509r1ovip1yssCbwodHw9e9hjIYLIpl8k57mT5uOVngTaH9MNfr
dWUwWBTL5J32Mn3ccrLAm4K7Ia7VasrYH3u3a057e/14zt5TdIb/nB3Am7Z3glytVrUD3rSxucWV
SkU74E2BtXUt5eFN/tUAG4ahHfCmpRXf8CO/rB3wpuOTCN+n01qJwxfeVCz+8LLPz4+ZDJdKJc8D
T/jCm/Bvy2q1eC98wPMLi54HnvDF+gUhlFFaqhacWgAAAABJRU5ErkJggg==</image>
      </value>
    </draw>
  </subform>
</subform></subform>
</template>

...

</xdp:xdp>
endstream
endobj
18 o obj

endobj
xref
0 19
0000000000 65535 f
0000000016 00000 n
0000000151 00000 n
0000000000 65535 f
0000000287 00000 n
0000000635 00000 n
0000000688 00000 n
0000000754 00000 n
0000000838 00000 n
0000000869 00000 n
0000000976 00000 n
0000001028 00000 n
0000001076 00000 n
0000001127 00000 n
0000001166 00000 n
0000001236 00000 n
0000001292 00000 n
0000001335 00000 n
trailer
<</Root 1 0 R  /Size 19>>
startxref
%%EOF

Note

The file templates/generic.xdp in com.farata.printing contains the generic template for XDP generation used for generation of h2.pdf and all other XDP samples from this chapter.

As you can see from Example 11.15, “A fragment of the h2.pdf content in XDP format”, the values of the text fields (Hello, World, etc.) are represented as XML text fields, which makes this PDF searchable. Note that the binary image is also presented in the encoded form as one of the XML elements.

Note

If you are developing not AIR but Flex applications, the client-side code can generate the entire XML portion of the Flex view components and send this XML to the server, where, say, Java Servlet puts it between the required PDF header and trailer and returns the entire document back to the web browser for printing.

The entire process of generation of this PDF in the XDP format is done by the following code from Example 11.14, “Saving data in XDP format: test_xdp2.mxml”:

var xdpDocument:XdpDocument=new XdpDocument();
xdpDocument.init(new PrintOptions());
var pdf:PDFHelper=new PDFHelper(xdpDocument);

pdf.createPDFPrologue();

pdf.createPage(canvas, PDFHelper.TYPE_PAGE);

pdf.createPDFEpilogue();

This code uses the helper components XdpDocument, PrintOptions, and PDFHelper, which are located in com.farata.printing.swc. The class PrintOptions is just a holder of such page parameters as orientation, margins, page size, and the like.

The MXML component XdpDocument implements a generic getter xdpContent, introduced in the beginning of this section. The source code of XDPDocument.mxml and a fragment of PDFHelper.as are shown in Examples 11.16 and 11.17, respectively. But these constitute just the tip of the iceberg, as they use uses dozens of supporting classes in the process of creation of the XDP content.

The good news is that unless the XDP format changes, you don’t need to learn all the nitty-gritty details, as we already did that tedious work of ensuring that the document is generated as required by the XDP specifications.

Example 11.16. Component XDPDocument.mxml

<?xml version="1.0" encoding="utf-8"?>

<xdp:XdpBaseObject
     xmlns:mx="http://www.adobe.com/2006/mxml"
     xmlns:xdp="com.farata.printing.pdf.xdp.*">

    <mx:XML id="xmlGen" source="/templates/generic.xdp"/>

<mx:Script><![CDATA[

import mx.core.UIComponent;

import com.farata.printing.pdf.buffered.PDFHelper;
    import com.farata.printing.geom.PNumber;
import mx.core.Container;
import com.farata.printing.geom.PSize;
import com.farata.printing.PrintOptions;
import com.farata.printing.geom.PRectangle;
import com.farata.printing.PaperSize;

public static var ns_xdp     : Namespace = new
Namespace("http://ns.adobe.com/xdp/");

public static var ns_xfat25  : Namespace = new Namespace(
                                          "http://www.xfa.org/schema/xfa-template/2.5/");

public static var ns_xci10   : Namespace = new
Namespace("http://www.xfa.org/schema/xci/1.0/");

public static var ns_xfals21 : Namespace = new
Namespace("http://www.xfa.org/schema/xfa-locale-set/2.1/");

public var paperSize   : PaperSize;
    public var margins     : PRectangle;
public var pageSize    : PSize;
public var orientation : String;
public var header:UIComponent;
public var footer:UIComponent;

public function get pages():Array{
   return children;
}

public override function get xdpContent():Object{

   var x:Object = xmlGen.copy();
   var f:Object = x..ns_xfat25::subform.(@name=="doc1")[0];
   var p:XML = <pageSet>
       <pageArea name="Page1" id="Page1">
      </pageArea>
   </pageSet>;

   var contentAreaX:Number = margins.left.px;
   var contentAreaY:Number = margins.top.px;
   var contentAreaH:Number = pageSize.height.px;
   var contentAreaW:Number = pageSize.width.px;

   if (header){
      var xdpHeader:XdpPage = new XdpPage();
      PDFHelper.createXdpPage(xdpHeader, header);

      xdpHeader.x = margins.left;
      xdpHeader.y = margins.top;
      contentAreaY = contentAreaY + header.height;
      contentAreaH = contentAreaH - header.height;

      p.pageArea.appendChild(xdpHeader.xdpContent);
   }



   if (footer){
      var xdpFooter:XdpContainer = new XdpContainer();
      PDFHelper.createXdpPage(xdpFooter, footer);
      xdpFooter.x = margins.left;
      var y:Number = pageSize.height.px + margins.top.px - footer.height;
      xdpFooter.y = new PNumber(y, PNumber.UNIT_PX);
      contentAreaH = contentAreaH - footer.height;

      p.pageArea.appendChild(xdpFooter.xdpContent);
   }

   p.pageArea.contentArea.@x = _pos(new PNumber(contentAreaX, PNumber.UNIT_PX));
   p.pageArea.contentArea.@y = _pos(new PNumber(contentAreaY, PNumber.UNIT_PX));
   p.pageArea.contentArea.@w = _pos(new PNumber(contentAreaW, PNumber.UNIT_PX));
   p.pageArea.contentArea.@h = _pos(new PNumber(contentAreaH, PNumber.UNIT_PX));

   p.pageArea.medium.@stock = "custom";
   p.pageArea.medium.@short = _pos(paperSize.width);
   p.pageArea.medium.@long  = _pos(paperSize.height);

   if( orientation==PrintOptions.ORIENTATION_LANDSCAPE )
      p.pageArea.medium.@orientation = "landscape";

   p.setNamespace(ns_xfat25);
   f.appendChild(p);
   f = applyStdData(f);

   return x;
}

public function addPage(p:XdpPage):void{

   addChild(p);
   p.pageNumber = pages.length;
   p.w = pageSize.width;
   p.h = pageSize.height;
}

public function init(opt:PrintOptions):void{

   paperSize = opt.paperSize.copy();
   margins = opt.margins.copy();

   pageSize = opt.pageSize;
   orientation = opt.orientation;
}
]]></mx:Script>

</xdp:XdpBaseObject>

The ActionScript class PDFHelper has about 300 lines of code; you can see some fragments of it in Example 11.17, “Fragments of PDFHelper.as”. We don’t provide code explanations here, as teaching the internals of the XDP protocol is not the goal of this chapter.

Example 11.17. Fragments of PDFHelper.as

package com.farata.printing.pdf.buffered{

   public class PDFHelper{


       private static var prefix : Array =[["\%PDF-1.7"+ "\n"," 65535 f"],
         ["1 0 obj"+"\n"+
         "<</Type /Catalog /StructTreeRoot 9 0 R /MarkInfo <</Marked true>> /Pages
15 0 R /AcroForm 16 0 R /NeedsRendering true>>"+"\n"+
         "endobj"+"\n", " 00000 n"],
         ["2 0 obj"+"\n"+
         "<</Type /Page /MediaBox [0 0 612 792] /Resources 5 0 R /Contents 4 0 R
/StructParent 0 /StructParents 0 /Parent 15 0 R>>"+"\n"+
         "endobj"+"\n", " 00000 n"],
         [""," 65535 f"],
         ["4 0 obj"+"\n"+
         "<</Length 298>>"+"\n"+
         "stream"+"\n"+
         "BT"+"\n"+
         "/Content <</MCID 0>> BDC"+"\n"+
         "0.0 0.0 0.0 rg"+"\n"+
         "/RelativeColorimetric ri"+"\n"+
         "/T1_0 1.0 Tf"+"\n"+
         "10.0 0.0 0.0 10.0 72.0 720.0 Tm"+"\n"+
         "(Warning: This form is not supported at all with the current version of
Acrobat or Adobe Reader.) Tj"+"\n"+
         "0.0 -1.8 Td"+"\n"+
         "(Upgrade to the latest version for full support.) Tj"+"\n"+
         "0.0 -1.8 Td"+"\n"+
         "EMC"+"\n"+
         "ET"+"\n"+
         "endstream"+"\n"+
         "endobj"+"\n", " 00000 n"],
         ["5 0 obj"+"\n"+
         "<</Font 8 0 R /ProcSet [/PDF /Text]>>"+"\n"+
         "endobj"+"\n", " 00000 n"],
         ["6 0 obj"+"\n"+
         "<</Type /Encoding /BaseEncoding /WinAnsiEncoding>>"+"\n"+
         "endobj"+"\n"," 00000 n"],
         ["7 0 obj"+"\n"+
         "<</Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding 6 0
R>>"+"\n"+
         "endobj"+"\n"," 00000 n"],
...

      private var ba:ByteArray = new ByteArray();
      public var xdpDocument:XdpDocument;

      public function PDFHelper(xdpDocument:XdpDocument) {
         this.xdpDocument = xdpDocument;
      }

      public function get pdfContent():ByteArray{
          return ba;
       }

      public function createPDFPrologue():void{
         //write pdf document prefix
         var xref:String ="";

         for (var i:int = 0; i < prefix.length; i++) {
            ba.writeMultiByte( prefix[i][0], "iso-8859-1");
            var str:String = padZeroes(ba.length, 10);
            xref = xref.concat(str + prefix[i][1] + " \n");
         }

         var s:String = xdpDocument.xdpContent.toString();
         s = s.substr(0, s.lastIndexOf("</subform>"));
         ba.writeMultiByte( s, "iso-8859-1");
       }
      public function createPage(obj:Object, type:int):void{
         var page:XdpPage = new XdpPage();
         createXdpPage(page, obj, type);
         ba.writeMultiByte(String(page.xdpContent), "iso-8859-1");
      }

     public function createPDFEpilogue():void{
         var xx:XML = xdpDocument.xdpContent as XML;
         ba.writeMultiByte( "</subform>"+"\r"+"</template>"+"\r", "iso-8859-1");
         ba.writeMultiByte( xx..ns_xci10::config[0].toString().replace("
xmlns:xdp=\"http://ns.adobe.com/xdp/\"", "")+"\r", "iso-8859-1");
         ba.writeMultiByte( xx..ns_xfals21::localeSet[0].toString().replace("
xmlns:xdp=\"http://ns.adobe.com/xdp/\"", "")+"\r", "iso-8859-1");
         ba.writeMultiByte( "</xdp:xdp>"+"\r", "iso-8859-1");
         ba.writeMultiByte( "endstream"+"\r", "iso-8859-1");
         ba.writeMultiByte( "endobj"+"\r", "iso-8859-1");
         ba.writeMultiByte("18 o obj " +"\n" + /*streamLength + */"\n" +
"endobj"+"\n",  "iso-8859-1");

         //the footer for the pdf document
          var end:String = "xref"+"\n"+ "0 " + 19 +"\n";
         var closing:String = end +
               "0000000000 65535 f"+"\r"+
               "0000000016 00000 n"+"\r"+
...
              "trailer"+"\n"+
               "<</Root 1 0 R  /Size " + 19 +">>"+"\n"+
               "startxref"+"\n"+
               "%%EOF"+"\n";
         ba.writeMultiByte(closing , "iso-8859-1");
       }

      public static function createXdpPage(root:XdpPage, obj:Object,
                type:int = 1):void{

         obj = resolveXdp(obj);
         if (obj is Container){

            var c:Container=obj as Container;
            var count:int=c.numChildren;
            if (type == TYPE_LIST) {
               var page:XdpPage = new XdpPage();
            } else {
               page = new XdpContainer();
            }

            page.x = new PNumber(c.x, PNumber.UNIT_PX);
            page.y = new PNumber(c.y, PNumber.UNIT_PX);
            page.h = new PNumber(c.height, PNumber.UNIT_PX);
            page.w = new PNumber(c.width, PNumber.UNIT_PX);

            root.addChild(page);

            if (obj is FormItem){
               var formItemLabel:Label = (obj as FormItem).itemLabel;
               createXdpPage(page, formItemLabel);
            }

            for(var i:int=0; i < count; i++){

               createXdpPage(page, c.getChildAt(i));
            }
         } else if (obj is IXdpObject){

            root.addChild(obj as IXdpObject);
         } else if (obj is UIComponent){

            var uiComp:UIComponent = obj as UIComponent;
            var xdp:XdpBaseObject = XdpImage.grab(uiComp);
            xdp.x = new PNumber(uiComp.x, PNumber.UNIT_PX);
            xdp.y = new PNumber(uiComp.y, PNumber.UNIT_PX);

            // set the width and hight of UIComponent (i.e. image)
            // for proper image scaling and conversion of pixels
            xdp.w = new PNumber(uiComp.width, PNumber.UNIT_PX);
            xdp.h = new PNumber(uiComp.height, PNumber.UNIT_PX);
            root.addChild(xdp);
         }
      }

}

Note

The code in Examples 11.16 and 11.17 is for illustration purposes only, because detailed coverage of the XDP generation is out of the scope of this book. Complete source code of com.farata.printing.swc, however, is available in the CVS repository of the Clear Toolkit project at SourceForge.

Example 11.18, “AIR application test4_xdp.mxml” shows the source code of test4_xdp.mxml, the modified version of text4.mxml, but this code generates a PDF in XDP format. The Flex window and PDF look the same as in Figure 11.3, “Running test4.mxml (left) generates PDF hw4 (right)”.

Example 11.18. AIR application test4_xdp.mxml

<?xml version="1.0" encoding="utf-8"?>

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
creationComplete="onCreationComplete(event)">

   <mx:Style source="main.css"/>

   <mx:DataGrid id="dg" x="0" y="0" width="100%" height="100%">
       <mx:columns>
      <mx:DataGridColumn dataField="name" headerText="Name"/>
      <mx:DataGridColumn dataField="phone" headerText="Phone"/>
      <mx:DataGridColumn dataField="email" headerText="E-mail"/>
      </mx:columns>

        </mx:DataGrid>
   <mx:ApplicationControlBar width="100%">
     <mx:Label text="File name:"/>
     <mx:TextInput id="txtFileName" text="hw4.pdf"/>
     <mx:Button label="Save PDF" click="savePdf()"/>
   </mx:ApplicationControlBar>

   <mx:Script>
      <![CDATA[
      import com.farata.printing.PrintOptions;
      import com.farata.printing.pdf.xdp.XdpDocument;
      import com.farata.printing.pdf.buffered.PDFHelper;

      private function doSavePdf():void{
         saveToFile(txtFileName.text , createXdpContent());
      }
      private function createXdpContent():ByteArray {

         var xdpDocument:XdpDocument=new XdpDocument();
         xdpDocument.init(new PrintOptions());
         var pdf:PDFHelper=new PDFHelper(xdpDocument);

         pdf.createPDFPrologue();
         pdf.createPage(canvas, PDFHelper.TYPE_LIST);
         pdf.createPDFEpilogue();

         return  pdf.pdfContent;
      }

      private function saveToFile(file:String, ba:ByteArray):void{

         var fs:FileStream=new FileStream();
         var f:File=File.desktopDirectory.resolvePath(file);

         fs.open(f, FileMode.WRITE);
         try{
            fs.writeBytes(ba);
         }catch(e:*){
                     // Error processing goes here
         }

         fs.close();
      }

      private function onCreationComplete(evt:*):void{

      var array:Array=[];
      for(var i:int=1; i <= 300; i++){

         var obj:Object=new Object();
         obj.name="User " + i;
         obj.phone="555-219-227" + i;
           obj.email="user" + i + "@hello.world";
           obj.active=(i % 2) == 1;

         array.push(obj);
           }

      dg.dataProvider=arrat;
     }

   ]]>
   </mx:Script>
</mx:WindowedApplication>

Note

We decided to keep the name of the code sample as test4_xdp.mxml, and you can find all other samples (test1 to test5) in the Flash Builder project clientPdfAir.

The previous example illustrates the printing of 300 rows of data to demonstrate that the pagination works properly and each page in the PDF file shows the header of the DataGrid (Figure 11.4, “The second page of the generated PDF hw5.pdf”).

Figure 11.4. The second page of the generated PDF hw5.pdf

The second page of the generated PDF hw5.pdf

Adding Printing to the PharmaSales Application

In Chapter 9, Working with Adobe AIR, you learned how to create an occasionally connected AIR application. In this section, you’ll modify it a little bit, armed with new knowledge and printing components. That’s right, the Acme Pharm dispatcher should be able to print visitation data for all salespeople.

On the other hand, each salesperson will be able to print the data about his visits to medical offices without the need to be connected to the server.

Printing for Acme Pharm Dispatchers

You’ll take care of the dispatcher’s needs first. As you might remember from Chapter 9, Working with Adobe AIR, VisitSchedules.mxml is a web application written in Flex. This means that you won’t be able to save a generated PDF file on the client’s filesystem and will need to send it to the server, which will just bounce it back so that the web browser will recognize it as a PDF and do the rest.

The source code of this version of PharmaSales is located in two Flex Builder projects, air.offline.demo.print and air.offline.demo.web.print. You’ll need to start with the latter (don’t forget to start MySQL Server and the servlet container first; the example uses Apache Tomcat). Your web browser should show you the view, similar to that shown in Figure 11.5, “Running VisitSchedules.mxml”.

Figure 11.5. Running VisitSchedules.mxml

Running VisitSchedules.mxml

Click the Print button and Figure 11.6, “Generated PDF from VisitSchedules.mxml”’s PDF will show up.

Figure 11.6. Generated PDF from VisitSchedules.mxml

Generated PDF from VisitSchedules.mxml

The PDF has been generated, and for illustration purposes, we performed a search for the word “Sandy,” which was successfully found.

The web browser reports that the PDF came from the following URL:

http://localhost:8080/air.offline.demo.web.print/PDFServlet/dg.pdf

You’ll see the code of this Java servlet, PDFServlet, in a little bit, but in the meantime, take a peek at the Flex code fragment in VisitSchedules.mxml (Example 11.19, “Flex code fragment for PDF generation”), which is invoked by clicking the Print button.

Example 11.19. Flex code fragment for PDF generation

<mx:Button click="openPDF(dg)" label="Print"/>
[Bindable]


   public var collection:DataCollection;

   private function openPDF(uiObject:Object):void{

      var xdpDocument:XdpDocument=new XdpDocument();
      var options:PrintOptions=new PrintOptions();
      options.paperSize=PaperSize.A4;
      options.orientation=PrintOptions.ORIENTATION_LANDSCAPE;

      xdpDocument.init(options);
      var pdf:PDFHelper=new PDFHelper(xdpDocument);

      pdf.createPDFPrologue();
      pdf.createPage(uiObject, PDFHelper.TYPE_LIST);
      pdf.createPDFEpilogue();

      sendToServer(uiObject.id + ".pdf", pdf.pdfContent);
   }


   private function sendToServer(file:String, ba:ByteArray):void{

      var req:URLRequest = new URLRequest("PDFServlet/"+file);
      req.method = URLRequestMethod.POST;

      ba.compress();

      req.data = ba;

      navigateToURL(req, "_blank");
   }

The function openPDF() looks similar to savePdf() from Example 11.18, “AIR application test4_xdp.mxml”. It’ll generate a PDF in the XDP format for the DataGrid container. At this point, the generated PDF is located in memory in the ByteArray object in pdf.pdfContent.

Next, the function sendToServer() compresses this ByteArray and sends it to the Java servlet,PDFServlet, deployed on the server. The source code of PDFServlet (Example 11.20, “PDFServlet.java”) is located in the folder src/com/Farata/demo/pdf in the Flex Builder project air.offline.demo.web.print.

Example 11.20. PDFServlet.java

package com.farata.demo.pdf;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.zip.InflaterInputStream;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class PDFServlet extends javax.servlet.http.HttpServlet
                              implements javax.servlet.Servlet {

   static final long serialVersionUID = 1L;

   // The size of the reading block
   private static final int READ_BLOCK = 8192;

    public PDFServlet() {
      super();
   }

   protected void doPost(HttpServletRequest req, HttpServletResponse resp)
         throws ServletException, IOException {

      ServletInputStream in = req.getInputStream();
      InflaterInputStream iin = new InflaterInputStream(in);

      byte[] content = readAllFromInputStream(iin);

      resp.setContentType("application/pdf");
      resp.flushBuffer();

      resp.getOutputStream().write(content);
      resp.getOutputStream().close();
   }

   private byte[] readAllFromInputStream(InputStream is) throws IOException {

      // create channel for input stream
      ReadableByteChannel bc = Channels.newChannel(is);

      ByteBuffer bb = ByteBuffer.allocate(READ_BLOCK);

      while (bc.read(bb) != -1) {
         // get new buffer for read
         bb = resizeBuffer(bb);
      }

      bb.flip();

      return bb.array();
   }

   private ByteBuffer resizeBuffer(ByteBuffer in) {
      ByteBuffer result = in;

      if (in.remaining() < READ_BLOCK) {
         // create new buffer
         result = ByteBuffer.allocate(in.capacity() * 2);

         // set limit to current position in buffer and set position to zero.
         in.flip();

         // store the content of original buffer to new buffer
         result.put(in);
      }

      return result;

   }
}

In short, this Java servlet echoes received PDF content from the client, assigns to it the MIME type "application/pdf", and sends it right back without doing any other processing.

Start reading this code from the method doPost(), which opens an input stream pointing at the request object (HTTPRequest) that arrived from the browser. Because the arrived content has been compressed by the client, the servlet inflates it first and writes it right back to the response object (HTTPResponse).

All manipulations with buffering in the code above are done for I/O efficiency.

The main takeaway here is that the server-side code didn’t modify the received PDF object, but just sent it back as PDF content. Now it’s the web browser’s responsibility to engage its Acrobat Reader plug-in to display the document.

Printing for Acme Pharm Salespeople

Now consider the AIR application that salespeople use on a daily basis, either in connected or in disconnected mode. In this case, the generated PDF won’t even go to the server side, but it will be saved in the file on the local disk.

You still want to print the list of visits for a particular salesperson as a grid, but to make this report a little fancier, the program should add the name of the salesperson as a header and the logo of Acme Pharm in the footer’s area of the report.

After running the application PharmaSales.mxml from the air.offline.demo.print project filtering the data for visits done by Liz Anthony from February 5 to June 7 in 2009, click the Print button. The filtered data will be saved in the file dg.pdf at the local storage directory. Exact file location is displayed in the status bar, as shown in Figure 11.7, “After clicking the buttons Filter and Print”.

After the PDF file is created, this AIR application automatically starts the web browser and opens dg.pdf. Figure 11.8, “A generated local file with header and footer: dg.pdf” shows how it looks for the sample data.

The header of dg.pdf shows the date range and the footer—the logo of the company and some arbitrary text. The header and the footer will be repeated on each page in case of multipage printing.

Figure 11.7. After clicking the buttons Filter and Print

After clicking the buttons Filter and Print

The header component looks like Example 11.21, “Header.mxml”.

Example 11.21. Header.mxml

<?xml version="1.0" encoding="utf-8"?>

 <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="594" height="56">

  <mx:Label id="headerLabel" x="10" y="13" text="Visits by Liz Anthony for the
   period from 02/05/09 to 06/07/09" width="565" height="27" fontSize="16"
   fontFamily="Arial" fontWeight="bold"/>

 </mx:Canvas>

Figure 11.8. A generated local file with header and footer: dg.pdf

A generated local file with header and footer: dg.pdf

The footer components are shown in Example 11.22, “Footer.mxml”.

Example 11.22. Footer.mxml

<?xml version="1.0" encoding="utf-8"?>

<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="556" height="78"
   xmlns:ns1="com.farata.controls.*">

 <ns1:Image source="@Embed(source='assets/pharma_small.jpg')" x="10" y="10"
  width="75" height="57"/>

 <ns1:Label x="100" y="27" width="446" height="34" text="Acme Pharma - your
  reliable source of drugs" fontSize="19"/>

</mx:Canvas>

Example 11.23, “Code fragment for printing a data grid with visits from PharmaSales.mxml” is a code fragment from the PharmaSales.mxml application. It illustrates what’s happening when the user clicks on the Print button.

Example 11.23. Code fragment for printing a data grid with visits from PharmaSales.mxml

<mx:Canvas height="100%" width="100%">
   <mx:Panel title="Pharma Sales - Salesman" width="100%" height="100%">
     <mx:ControlBar>
      <mx:Label text="Select Date Range:"/>
         <mx:DateField id="dateRangeFrom"
                 enabled="{!showAll.selected}"/>
       <mx:DateField id="dateRangeTo"
                 enabled="{!showAll.selected}"/>
       <mx:CheckBox id="showAll" label="Show all"/>
       <mx:Button label="Filter" click="doFilter()"/>
   </mx:ControlBar>

   <fx:DataGrid toolTip="Double click for details" doubleClick="onDoubleClick()"
       doubleClickEnabled="true" horizontalScrollPolicy="auto" width="100%"
       id="dg" dataProvider="{visitDataCollection}" editable="true" height="100%">
...

   <mx:Button click="openVisitDataCollectionPDF(dg)" label="Print"/>

</mx:Canvas>

   <mx:Script>
      <![CDATA[

      import com.farata.printing.PaperSize;
      import com.farata.printing.PrintOptions;
      import com.farata.printing.pdf.xdp.XdpDocument;
      import com.farata.printing.pdf.buffered.PDFHelper;

   private function openVisitDataCollectionPDF(uiObject:Object):void{

            var xdpDocument:XdpDocument=new XdpDocument();
            var options:PrintOptions=new PrintOptions();
            options.paperSize=PaperSize.A4;
            options.orientation=PrintOptions.ORIENTATION_LANDSCAPE;
            xdpDocument.init(options);

            //Create header text dynamically

      var text:String="";

      var df:DateFormatter=new DateFormatter();
      if (showAll.selected || (!dateRangeFrom.selectedDate &&
                                 !dateRangeTo.selectedDate)){
         text="All visits by " + username.text;
      } else if (!dateRangeFrom.selectedDate && dateRangeTo.selectedDate){
         text="Visits by " + username.text + " to " +
                   df.format(dateRangeTo.selectedDate);
      } else if (dateRangeFrom.selectedDate && !dateRangeTo.selectedDate){
             text="Visits by " + username.text + " from " +
                   df.format(dateRangeFrom.selectedDate);
      } else {
           text="Visits by " + username.text + " from " +
                   df.format(dateRangeFrom.selectedDate) + " to " +
                   df.format(dateRangeTo.selectedDate);
         }

        var header:Header=new Header();
      header.initialize();
        header.headerLabel.text=text;

      xdpDocument.header=header;
      xdpDocument.footer=new Footer();
      xdpDocument.footer.initialize();

      var pdf:PDFHelper=new PDFHelper(xdpDocument);

      pdf.createPDFPrologue();

      pdf.createPage(uiObject, PDFHelper.TYPE_LIST);

      pdf.createPDFEpilogue();

      savePDF(uiObject.id + ".pdf", pdf.pdfContent);
   }

   private function savePDF(file:String, ba:ByteArray):void {

      var fs:FileStream=new FileStream();
      var f:File=File.applicationStorageDirectory.resolvePath(file);

        try{
        fs.open(f, FileMode.WRITE);
        fs.writeBytes(ba);

           var req:URLRequest=new URLRequest(f.url);
        navigateToURL(req, "_blank");
        status="Saved to " + f.nativePath;

      } catch(e:*){
         status=e.message;
        } finally {
         fs.close();
       }

   }
   ]]>

   </mx:Script>

</mx:WindowedApplication>

The Visit Details window now has the Print button, too, as you can see in Figure 11.9, “Visit Details with the Print button”.

The produced PDF file looks like Figure 11.10, “The generated PDF file details_panel.pdf”.

Figure 11.9. Visit Details with the Print button

Visit Details with the Print button

Figure 11.10. The generated PDF file details_panel.pdf

The generated PDF file details_panel.pdf

There is no reason why a Flex window with Google Maps can’t have the Print button, as shown in Figure 11.11, “A Google Maps window with the Print button”.

Figure 11.11. A Google Maps window with the Print button

A Google Maps window with the Print button

The generated PDF with the map is shown in Figure 11.12, “A Google map in a generated PDF”.

The PDF files for both the Visit Details and map windows are generated by similar functions, as shown in Example 11.24, “Functions for generation of Visit Details and map PDFs”.

Example 11.24. Functions for generation of Visit Details and map PDFs

private function openVisitPDF(uiObject:Object):void{

   var xdpDocument:XdpDocument=new XdpDocument();

     var options:PrintOptions=new PrintOptions();
   options.paperSize=PaperSize.A4;
   options.orientation=PrintOptions.ORIENTATION_LANDSCAPE;

   xdpDocument.init(options);
   xdpDocument.footer=new Footer();
   xdpDocument.footer.initialize();

   var pdf:PDFHelper=new PDFHelper(xdpDocument);
   pdf.createPDFPrologue();
   pdf.createPage(uiObject, PDFHelper.TYPE_PAGE);
   pdf.createPDFEpilogue();

   savePDF(uiObject.id + ".pdf", pdf.pdfContent);
}


private function openGoogleMapPDF(uiObject:Object):void{

   var xdpDocument:XdpDocument=new XdpDocument();

   var options:PrintOptions=new PrintOptions();
   options.paperSize=PaperSize.A4;
   options.orientation=PrintOptions.ORIENTATION_LANDSCAPE;

   xdpDocument.init(options);
   xdpDocument.footer=new Footer();
   xdpDocument.footer.initialize();

   var pdf:PDFHelper=new PDFHelper(xdpDocument);

   pdf.createPDFPrologue();
   pdf.createPage(uiObject, PDFHelper.TYPE_PAGE);
   pdf.createPDFEpilogue();

   savePDF(uiObject.id + ".pdf", pdf.pdfContent);
}

Figure 11.12. A Google map in a generated PDF

A Google map in a generated PDF

ClearBI: A Web Reporter for Flex

If you want to make more professional-looking reports with such features as adding formulas, creating totals and subtotals, exporting to Microsoft Excel, and charting, consider using the ClearBI reporter that will be included in a future version of the Clear Toolkit framework. To run these reports, end users don’t need anything but Flash Player–enabled web browsers.

Flex developers use ClearBI’s Designer (an AIR application) to create custom reports that can either be saved on the local drives or published in a DBMS.

More advanced business users can customize their reports right inside the web browser. For example, Figure 11.13, “A sample report with grouping” depicts a report with grouping by state, and departments with calculated totals and subtotals.

Figure 11.13. A sample report with grouping

A sample report with grouping

When the user directs a web browser to a deployed ClearBI report player application (a SWF file; it’ll arrive with an extra toolbar—see the toolbar below the address bar in Figure 11.13, “A sample report with grouping”) that allows users to zoom in, export to Microsoft Excel, generate PDFs, and open a Designer view that allows you to create charts, grouping, filters, sorting, and format masks; compute fields; and introduce formulas. Figure 11.14, “ClearBI Designer” depicts a Designer view with a formula, converting department codes into titles.

ClearBI Designer can be invoked either by the user inside the web browser, or by any junior developer as a standalone AIR application (no knowledge of Flex is required).

ClearBI supports user roles and hierarchies to specify who can access specific reports.

Figure 11.14. ClearBI Designer

ClearBI Designer

Summary

In this chapter, you’ve learned how to extend Flex components to give them PDF generation capabilities. We encourage you to experiment in this area so you can be in full control of your own set of printable controls that reflect all printing needs of your enterprise. On the other hand, we offer you our version of such components, which are included in Clear Toolkit components—ready to use.

The principle of PDF generation on the client described in this chapter has several advantages:

  • You don’t have to create separate templates for further merging with data—everything is happening at the component level.

  • Pagination of the long documents is also taken care of by the client-side code.

  • Produced documents are fully searchable.

  • If you sent editable components from Flex (e.g., a DataGrid), they will remain editable in the PDF document, too.

All source code for the examples used in this chapter is located under the Flex Builder projects air.offline.demo.print, air.offline.demo.web.print, PrintingSamples, and clientPdfAir. We’ve also included for your convenience the source code of the package com.farata.printing, which is a part of clear.swc. But to get the up-to-date version of the code of all components included in this library, visit the SourceForge repository of the Clear Toolkit framework at https://sourceforge.net/projects/cleartoolkit/.

Wouldn’t you agree that with our smart Flex components, the task of printing becomes almost trivial?

If you enjoyed this excerpt, buy a copy of Enterprise Development with Flex.

 

Posted by 1010
반응형

Printing Flex Data Grids

One of the questions that I was asked at Colorado Software Summit 2008 after one of my three presentations of Applying Flash to Java: Flex and OpenLaszlo was if there is a way to print the contents of a DataGrid. In this blog entry, I demonstrate simple application of the Flex PrintDataGrid control for doing just that.

The PrintDataGrid control component and the closely related PrintAdvancedDataGrid control component are components meant for visualization only. While they each inherit properties and behaviors from their respective parent classes (DataGrid and AdvancedDataGrid), the print-specific classes should not use any functionality that is associated with user interaction. Note also that the print-specific child classes are in the mx.printing package while their parents are in the mx.controls package.

One of the particularly useful aspects of the Flex 3 Language Reference is that it often includes example code for the language items being described. When the Flex item being described is visual, this reference often includes an actual running instantiation of the example code via the Flash Player. Fortunately, this is true of the PrintDataGrid, which includes multiple examples and does include an actual running Flash-based rendering of the examples.

I am basing my example of using the PrintDataGrid on the DataGrid I previously used in the blog entry Communication Between Two Flex Applications. Here, on one file called SimplePrintDataGridExample.mxml, is the entire code listing demonstrating a basic use of PrintDataGrid.

SimplePrintDataGridExample.mxml

  1. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"  
  2.                 viewSourceURL="http://localhost:8080/Css2008FlashExamples/flex/SimpleDataGrid/SimplePrintDataGridExample.mxml">  
  3.   
  4.    <mx:Script>  
  5.    <![CDATA[ 
  6.    import mx.controls.Alert; 
  7.    import flash.printing.PrintJobOrientation; 
  8.    import mx.printing.FlexPrintJob; 
  9.    import mx.printing.FlexPrintJobScaleType; 
  10.    import mx.printing.PrintDataGrid; 
  11.  
  12.    /** 
  13.     * Prints contents of DataGrid in print-friendly format. 
  14.     * 
  15.     * <p>ADDITIONAL REFERENCES:</p> 
  16.     * http://livedocs.adobe.com/flex/3/langref/mx/printing/FlexPrintJob.html 
  17.  
  18.     * http://livedocs.adobe.com/flex/3/html/printing_3.html 
  19.     */ 
  20.    public function printDataGridContents():void 
  21.    { 
  22.       const printJob:FlexPrintJob = new FlexPrintJob(); 
  23.  
  24.       if ( printJob.start() ) 
  25.       { 
  26.          const printDataGrid:PrintDataGrid = new PrintDataGrid(); 
  27.          printDataGrid.width = printJob.pageWidth; 
  28.          printDataGrid.height = printJob.pageHeight; 
  29.          printDataGrid.columns = dataGrid.columns; 
  30.          printDataGrid.dataProvider = presenters.copy(); 
  31.          printDataGrid.visible = false; 
  32.          Application.application.addChild(printDataGrid); 
  33.          while (printDataGrid.validNextPage) 
  34.          { 
  35.             printDataGrid.nextPage(); 
  36.             printJob.addObject(printDataGrid); 
  37.          } 
  38.          printJob.send(); 
  39.          Application.application.removeChild(printDataGrid); 
  40.       } 
  41.    } 
  42.    ]]>  
  43.    </mx:Script>  
  44.   
  45.    <mx:XMLList id="presenters">  
  46.      <presenter>  
  47.        <lastname>Johnsson</lastname>  
  48.        <firstname>Dan Bergh</firstname>  
  49.        <organization>OmegaPoint AB</organization>  
  50.        <url>http://softwaresummit.org/2008/speakers/johnsson.htm</url>  
  51.      </presenter>  
  52.      <presenter>  
  53.        <lastname>Kaplan-Moss</lastname>  
  54.        <firstname>Jacob</firstname>  
  55.        <organization>Whiskey Media</organization>  
  56.        <url>http://softwaresummit.org/2008/speakers/kaplan-moss.htm</url>  
  57.      </presenter>  
  58.      <presenter>  
  59.        <lastname>Lan</lastname>  
  60.        <firstname>Ikai</firstname>  
  61.        <organization>LinkedIn</organization>  
  62.        <url>http://softwaresummit.org/2008/speakers/lan.htm</url>  
  63.      </presenter>  
  64.      <presenter>  
  65.        <lastname>Marx</lastname>  
  66.        <firstname>Dustin</firstname>  
  67.        <organization>Raytheon Company</organization>  
  68.        <url>http://softwaresummit.org/2008/speakers/marx.htm</url>  
  69.      </presenter>  
  70.      <presenter>  
  71.        <lastname>Morrill</lastname>  
  72.        <firstname>Dan</firstname>  
  73.        <organization>Google</organization>  
  74.        <url>http://softwaresummit.org/2008/speakers/morrill.htm</url>  
  75.      </presenter>  
  76.      <presenter>  
  77.        <lastname>Moskowitz</lastname>  
  78.        <firstname>David</firstname>  
  79.        <organization>Productivity Solutions, Inc.</organization>  
  80.        <url>http://softwaresummit.org/2008/speakers/moskowitz.htm</url>  
  81.      </presenter>  
  82.      <presenter>  
  83.        <lastname>Opstvedt</lastname>  
  84.        <firstname>Hermod</firstname>  
  85.        <organization>DnB NOR</organization>  
  86.        <url>http://softwaresummit.org/2008/speakers/opstvedt.htm</url>  
  87.      </presenter>  
  88.    </mx:XMLList>  
  89.   
  90.    <mx:Panel id="mainPanel" width="75%"  
  91.              title="CSS 2008 DataGrid Example">  
  92.       <mx:DataGrid id="dataGrid"  
  93.                    width="100%" height="100%"  
  94.                    rowCount="5" dataProvider="{presenters}">  
  95.          <mx:columns>  
  96.             <mx:DataGridColumn dataField="lastname"  
  97.                                headerText="Last Name"  
  98.                                width="75" />  
  99.             <mx:DataGridColumn dataField="firstname"  
  100.                                headerText="First Name"  
  101.                                width="75" />  
  102.             <mx:DataGridColumn dataField="organization"  
  103.                                headerText="Organization"  
  104.                                width="100" />  
  105.             <mx:DataGridColumn dataField="url"  
  106.                                headerText="CSS URL"  
  107.                                width="200" />  
  108.          </mx:columns>  
  109.       </mx:DataGrid>  
  110.       <mx:Button click="printDataGridContents();" label="Print!" />  
  111.    </mx:Panel>  
  112.   
  113. </mx:Application>  


The original DataGrid (the one intended for interaction and not for printing that is defined in MXML rather than in ActionScript) is essentially the same as the one I used in the Flex-to-Flex Communication blog entry. A button has been added in this example to kick off the ActionScript method printDataGridContents().

The ActionScript method that is invoked when the button is presssed only directly associate the original, interactive DataGrid with the PrintDataGrid to get column names for the PrintDataGrid. For the source data, the PrintDataGrid is associated (via dataProvider property) with the same XML data that backs the original, interactive grid. Because I don't want the PrintDataGrid affecting the original XML, I used the XMLList.copy() function.

This example also demonstrates how the width and height of the PrintDataGrid can be associated with the FlexPrintJob's width and height.

The following two screen snapshots demonstrate how the Flex application whose code is listed above appears when it is first started and when the Print! button is clicked.


Initial Appearance of SimplePrintDataGridExample



Print! Option Selected via Button Click



When a printer is selected, the print-out of this data looks like that shown in the next screen snapshot.


Printed Output



The Google Chrome web browser was used for all of the above screen snapshots. One of the positive features of Google Chrome is its Developer's "View source" capability. The regular "View Source" capability shows the MXML source code without any translation of the original text and is not very appealing. However, one can choose Develooer->View source on that unappealing display of source to see source with color coding and line numbers as shown in the next screen snapshot.


Google Chrome Display of Developer->View source




I had explicitly made source code available for this Flex application (a process I described in a previous blog entry) so it is nice to have that source code displayed with color coding and line numbers.


Printing from Flex/Flash is Not Without Challenges

In my example here, I depended on the user (me!) to choose to print out in "landscape" mode. Had I printed in "portrait" mode, not all grid information would have been printed. Jesse Warden has a blog entry Forcing Landscape Printing in Flex 2 and 3 that talks about some of the challenges associated with trying to print in landscape mode from Flex.

Even though FlexPrintJob is easy to use, printing from Flex (or more specifically from Flash) can still be tricky, especially when there are multiple pages involved. Some efforts to make this easier include FlexReport and Lin Lin: Tips for using PrintingJob in flex 1.5. Of course, printing has been a difficult issue for many fine programming languages.


Conclusion

The FlexPrintJob object and associated components intended specifically for printing make it possible with relatively few lines of code to print out Flex data. I have found that it does require some trial-and-error to get the printing where I want it. There are also issues of how the user attempts to print (using the web browers's Print icon or option rather than the Flex application provided print capability), but these can be at least partially handled via training and/or use of JavaScript to disable those browser print features. Also, another nice option is to mix Flex with DHTML and have the print support be implemented with a static HTML page created specifically for printing.
Posted by 1010
반응형

PrintDataGrid,percentHeight and multipage layouts

Categories: actionscript, flex
Written By: sebi

I rarely had to create any complex layouts for printing with Flex, until now.
The layout has to have different pages, with different elements on them. The issue is that the PrintDataGrid calculated it’s height right (well, almost) at the first page, but maintained this value to the rest of the pages – instead of recalculating based on that structures.

There is a chapter in the Flex help discussing this scenario: Using the PrintDataGrid control for multipage grids.

I implemented the whole print layout view based on this description, and quickly realized that even the result is not exactly what I wanted.

Print result

Print result

The help suggest something that sounds relevant to this issue, but it didn’t solve my problem.

When you use a PrintDataGrid control to print a single data grid across multiple pages, you queue each page of the grid individually. If your application customizes each page beyond simply using the nextPage() method to page through the PrintDataGrid, you must call the validateNow() method to update the page layout before you print each page, as shown in Print output component.

The root of the problem is that the PrintDataGrid’s setActualSize contains an interesting line, which resets the percentHeight of the grid to NaN. As a result, the parent container of the grid won’t resize the grid, even if it’s layout changes, and we call validateNow().

	/**
	 *  @private
	 *  setActualSize() is overridden to update _originalHeight.
	 */
	override public function setActualSize(w:Number, h:Number):void
	{
		if (!isNaN(percentHeight))
		{
			_originalHeight = h;
			super.percentHeight = NaN;
			measure();
			h = measuredHeight;
		}
 
		super.setActualSize(w, h);
 
		invalidateDisplayList();
 
		if (sizeToPage && !isNaN(explicitHeight))
			explicitHeight = NaN;
	}

The whole resizing cycle of the grid seemed to be quite complex, so I decided to look for an easier solution than trying to extend-override some of it’s steps, so ended up with the following:

	public function showPage( pageType:String ):void 
	{
		switch( pageType )
		{
			case "first":
			case "single":
				meetingInfoExtended.visible 		= true;
				meetingInfoExtended.includeInLayout	= true;
				meetingInfo.visible 		= false;
				meetingInfo.includeInLayout= false;						
				break;
			case "middle":
			case "last":
				meetingInfoExtended.visible 		= false;
				meetingInfoExtended.includeInLayout	= false;
				meetingInfo.visible 		= true;
				meetingInfo.includeInLayout= true;						
				break; 
		}
		printingGrid.percentHeight = 100;
		validateNow();
	}

Basically re-apply the percentHeight setting in every paging, forcing the grid to recalculate it’s size.

There was another small glitch, what caused a scrollbar to appear in the print layout – at least it tried to appear. I have a multi-line text there, an mx:Text, and I think the text couldn’t calculate it height fast enough, but if I didn’t pass a fixed height to it, the grid miss-calculated it’s height. But only the text caused this problem with the percent heights, other components could handle it.

One Response to “PrintDataGrid,percentHeight and multipage layouts”

  1. Robert Cesaric Says:

    Thanks for post.. We were just building a print view that head custom header/footer and had the same issue. Your percentHeight fix worked perfectly.

    We’re still noticing issues priting with the datagrid variableRowHeight but I’m much happier getting this bug fixed first. Thx!

Posted by 1010
반응형

RichTextEditor control

The RichTextEditor control lets users enter, edit, and format text. Users apply text formatting and URL links by using subcontrols that are located at the bottom of the RichTextEditor control.

For complete reference information, see the Adobe Flex Language Reference.

About the RichTextEditor control

The RichTextEditor control consists of a Panel control with two direct children:

  • A TextArea control in which users can enter text
  • A tool bar container with format controls that let a user specify the text characteristics. Users can use the tool bar subcontrols to apply the following text characteristics:
    • Font family
    • Font size

      Note: When using the RichTextEditor control, you specify the actual pixel value for the font size. This size is not equivalent to the relative font sizes specified in HTML by using the size attribute of the HTML <font> tag.

       

    • Any combination of bold, italic and underline font styles
    • Text color
    • Text alignment: left, center, right, or justified
    • Bullets
    • URL links

The following image shows a RichTextEditor control with some formatted text:

RichTextEditor control with some formatted text

For the source for this example, see Creating a RichTextEditor control.

You use the RichTextEditor interactively as follows:

  • Text that you type is formatted as specified by the control settings.
  • To apply new formatting to existing text, select the text and set the controls to the required format.
  • To create a link, select a range of text, enter the link target in the text box on the right, and press Enter. You can only specify the URL; the link always opens in a _blank target. Also, creating the link does not change the appearance of the link text; you must separately apply any color and underlining.
  • You can cut, copy, and paste rich text within and between Flash HTML text fields, including the RichTextEditor control's TextArea subcontrol, by using the normal keyboard commands. You can copy and paste plain text between the TextArea and any other text application, such as your browser or a text editor.

Creating a RichTextEditor control

You define a RichTextEditor control in MXML by using the <mx:RichTextEditor> tag, as the following example shows. Specify an id value if you intend to refer to a control elsewhere in your MXML, either in another tag or in an ActionScript block.

<?xml version="1.0"?>
<!-- textcontrols/RichTextEditorControl.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:RichTextEditor id="myRTE" text="Congratulations, winner!" />
</mx:Application>

The executing SWF file for the previous example is shown below:

 

You can use the text property to specify an unformatted text string, or the htmlText property to specify an HTML-formatted string. For more information on using these properties, see Using the text property, and Using the htmlText property. For information on selecting, replacing, and formatting text that is in the control, see Selecting and modifying text.

The following example shows the code used to create the image in About the RichTextEditor control:

<?xml version="1.0"?>
<!-- textcontrols/RichTextEditorControlWithFormattedText.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <!-- The HTML text string used to populate the RichTextEditor control's
     TextArea subcontrol. The text is on a single line. -->
  <mx:Script><![CDATA[
     [Bindable]
     public var htmlData:String="<textformat leading='2'><p align='center'><b><font size='20'>HTML Formatted Text</font></b></p></textformat><br><textformat leading='2'><p align='left'><font face='_sans' size='12' color='#000000'>This paragraph contains<b>bold</b>, <i>italic</i>, <u>underlined</u>, and <b><i><u>bold italic underlined </u></i></b>text.</font></p></textformat><br><p><u><font face='arial' size='14' color='#ff0000'>This a red underlined 14-point arial font with no alignment set.</font></u></p><p align='right'><font face='verdana' size='12' color='#006666'><b>This a teal bold 12-pt.' Verdana font with alignment set to right.</b></font></p><br><li>This is bulleted text.</li><li><font face='arial' size='12' color='#0000ff'><u> <a href='http://www.adobe.com'>This is a bulleted link with underline and blue color set.</a></u></font></li>";
  ]]></mx:Script>

  <!-- The RichTextEditor control. To reference a subcontrol prefix its ID with the RichTextEditor control ID. -->
  <mx:RichTextEditor id="rte1" 
     backgroundColor="#ccffcc" 
     width="500"
     headerColors="[#88bb88, #bbeebb]" 
     footerColors="[#bbeebb, #88bb88]" 
     title="Rich Text Editor" 
     htmlText="{htmlData}" 
     initialize="rte1.textArea.setStyle('backgroundColor', '0xeeffee')"
  />
</mx:Application>

The executing SWF file for the previous example is shown below:

 

Sizing the RichTextEditor control

The control does not resize in response to the size of the text in the TextArea control. If the text exceeds the viewable space, by default, the TextArea control adds scroll bars. If you specify a value for either the height or width property but not both, the control uses the default value for the property that you do not set.

If you set a width value that results in a width less than 605 pixels wide, the RichTextEditor control stacks the subcontrols in rows.

Programming RichTextEditor subcontrols

Your application can control the settings of any of the RichTextEditor subcontrols, such as the TextArea, the ColorPicker, or any of the ComboBox or Button controls that control text formatting. To refer to a RichTextEditor subcontrol, prefix the requested control's ID with the RichTextEditor control ID. For example, to refer to the ColorPicker control in a RichTextEditor control that has the ID rte1, use rte1.colorPicker.

Inheritable styles that you apply directly to a RichTextEditor control affect the underlying Panel control and the subcontrols. Properties that you apply directly to a RichTextEditor control affect the underlying Panel control only.

For more information, see the RichTextEditor in the Adobe Flex Language Reference.

Setting RichTextEditor subcontrol properties and styles

The following simple code example shows how you can set and change the properties and styles of the RichTextEditor control and its subcontrols. This example uses styles that the RichTextEditor control inherits from the Panel class to set the colors of the Panel control header and the tool bar container, and sets the TextArea control's background color in the RichTextEditor control's creationComplete event member. When users click the buttons, their click event listeners change the TextArea control's background color and the selected color of the ColorPicker control.

<?xml version="1.0"?>
<!-- textcontrols/RTESubcontrol.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
     height="420">
  <!-- The RichTextEditor control. To set the a subcontrol's style or property, fully qualify the control ID. The footerColors style sets the ControlBar colors. -->
  <mx:RichTextEditor id="rte1" 
     backgroundColor="#ccffcc" 
     headerColors="[#88bb88, #bbeebb]" 
     footerColors="[#bbeebb, #88bb88]" 
     title="Rich Text Editor" 
     creationComplete="rte1.textArea.setStyle('backgroundColor','0xeeffee')" 
     text="Simple sample text"
  />

  <!-- Button to set a white TextArea background. -->
  <mx:Button 
     label="Change appearance" 
     click="rte1.textArea.setStyle('backgroundColor', '0xffffff');rte1.colorPicker.selectedIndex=27;"
  />

  <!-- Button to reset the display to its original appearance. --> 
  <mx:Button 
     label="Reset Appearance" 
     click="rte1.textArea.setStyle('backgroundColor', '0xeeffee');rte1.colorPicker.selectedIndex=0;"
  />
</mx:Application>

The executing SWF file for the previous example is shown below:

 

Removing and adding RichTextEditor subcontrols

You can remove any of the standard RichTextEditor subcontrols, such as the alignment buttons. You can also add your own subcontrols, such as a button that pops up a find-and-replace dialog box.

Remove an existing subcontrol

  1. Create a function that calls the removeChildAt method of the editor's tool bar Container subcontrol, specifying the control to remove.
  2. Call the method in the RichTextEditor control's initialize event listener.

The following example removes the alignment buttons from a RichTextEditor control, and shows the default appearance of a second RichTextEditor control:

<?xml version="1.0"?>
<!-- textcontrols/RTERemoveAlignButtons.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Script><![CDATA[
        public function removeAlignButtons():void {
            rt1.toolbar.removeChild(rt1.alignButtons);
        }
    ]]></mx:Script>
  
    <mx:HBox>
        <mx:RichTextEditor id="rt1" 
            title="RichTextEditor With No Align Buttons" 
            creationComplete="removeAlignButtons()"
        />
        <mx:RichTextEditor id="rt2" 
            title="Default RichTextEditor"
        />
    </mx:HBox>
  
</mx:Application>

The executing SWF file for the previous example is shown below:

 

Add a new subcontrol

  1. Create an ActionScript function that defines the subcontrol. Also create any necessary methods to support the control's function.
  2. Call the method in the RichTextEditor control's initialize event listener, as in the following tag:
    <mx:RichTextEditor id="rt" initialize="addMyControl()"
    
    

The following example adds a find-and-replace dialog box to a RichTextEditor control. It consists of two files: the application, and a custom TitleWindow control that defines the find-and-replace dialog (which also performs the find-and-replace operation on the text). The application includes a function that adds a button to pop up the TitleWindow, as follows:

<?xml version="1.0"?>
<!-- textcontrols/CustomRTE.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Script>
        <![CDATA[
            import mx.controls.*;
            import mx.containers.*;
            import flash.events.*;
            import mx.managers.PopUpManager;
            import mx.core.IFlexDisplayObject;

            /* The variable for the pop-up dialog box. */
            public var w:IFlexDisplayObject;

            /* Add the Find/Replace button to the Rich Text Editor control's
               tool bar container. */
            public function addFindReplaceButton():void {
                var but:Button = new Button();
                but.label = "Find/Replace";
                but.addEventListener("click",findReplaceDialog);
                rt.toolbar.addChild(but);
            }

            /* The event listener for the Find/Replace button's click event
               creates a pop-up with a MyTitleWindow custom control. */
            public function findReplaceDialog(event:Event):void {
                var w:MyTitleWindow = MyTitleWindow(
                    PopUpManager.createPopUp(this, MyTitleWindow, true)
                );
                w.height=200;
                w.width=340;

                /* Pass the a reference to the textArea subcontrol
                   so that the custom control can replace the text. */
                w.RTETextArea = rt.textArea;
                PopUpManager.centerPopUp(w);
            }
        ]]>
    </mx:Script>

    <mx:RichTextEditor id="rt" width="95%" 
        title="RichTextEditor"
        text="This is a short sentence."
        initialize="addFindReplaceButton();"
    />

</mx:Application>

The executing SWF file for the previous example is shown below:

 

The following MyTitleWindow.mxml file defines the custom myTitleWindow control that contains the find-and-replace interface and logic:

<?xml version="1.0"?>
<!-- A TitleWindow that displays the X close button. Clicking the close button 
only generates a CloseEvent event, so it must handle the event to close the control. -->
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" 
    title="Find/Replace" 
    showCloseButton="true" 
    close="closeDialog();"
>
    <mx:Script>
        <![CDATA[
            import mx.controls.TextArea;
            import mx.managers.PopUpManager;

            /* Reference to the RichTextArea textArea subcontrol.
               It is set by the application findReplaceDialog method
               and used in the replaceAndClose method, below. */
            public var RTETextArea:TextArea;

            /* The event handler for the Replace button's click event.
               Replace the text in the RichTextEditor TextArea and
               close the dialog box. */
            public function replaceAndClose():void{
                RTETextArea.text = RTETextArea.text.replace(ti1.text, ti2.text);
                PopUpManager.removePopUp(this);
            }

            /* The event handler for the TitleWindow close button. */
            public function closeDialog():void {
                PopUpManager.removePopUp(this);
            }

        ]]>
    </mx:Script>

    <!-- The TitleWindow subcontrols: the find and replace inputs, 
         their labels, and a button to initiate the operation. -->
    <mx:Label text="Find what:"/>
    <mx:TextInput id="ti1"/>
    
    <mx:Label text="Replace with:"/>
    <mx:TextInput id="ti2"/>
    
    <mx:Button label="Replace" click="replaceAndClose();"/> 
</mx:TitleWindow>

Posted by 1010
반응형

Simple TLF Text Editor released

I just released a simple TLF-based text editor. It allows the end user to have a familiar interface and generates TextFlow on the fly as the user types. It’s simple and light-weight and supports most commonly used styles. It’s lighter weight than Adobe’s tlf text editor. This editor is perfect to use in a CMS.

I even implemented what I’m calling “soft bullets”. TLF 1.x doesn’t officially support bullets so I implemented it myself. The reason it’s soft is because you can insert the cursor before the bullet and type text and there are some other strange quirks, but it’s good enough to give the end user some decent styling options.

The source is released under MIT license, so feel free to use it in your apps and modify it for your needs. You can get the code on Google code.

This entry was posted in Components, TLF. Bookmark the permalink.

 

Posted by 1010