6 Replies Latest reply on Jun 2, 2009 5:01 PM by diazruy

    Loading a font at runtime from a different domain

    diazruy

      Hi, I have been struggling with trying to figure out how to get this to work. I am fairly new to Flash so it's quite possible that I am missing some obvious bit of code.

       

      What I am working on in a component that will load a SWF file at runtime via HTTP from a different domain as where the main file is running. The loaded SWF file has an embedded Font that I want to be available in the main SWF. As long as the font.swf is on the same domain as the main.swf, everything works fine. But as as soon as I put the font.swf on a different server (Amazon S3), I get the following error when I call Font.registerFont( myFont ) in main.swf:


      ArgumentError: Error #1508: The value specified for argument font is invalid.
          at flash.text::Font$/registerFont()

       

      So, in trying to find a solution, I think it has to do with cross domain policies. So I have created a crossdomain.xml which resides in the root of my Amazon S3 bucket. The content of the crossdomain.xml is currently:


      <?xml version="1.0"?>
      <!DOCTYPE cross-domain-policy
      SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
      <cross-domain-policy>
          <allow-access-from domain="*" />
      </cross-domain-policy>

       

      I also tried adding a specific entry for the domain I am serving main.swf from. After doing this, I got the same result.

       

      I might add that I am not getting SecurityExceptions thrown at any point. The SWF is correctly retrieved and in fact I am even able to call methods on the retrieved SWF, it's just the registerFont that complains. My main.swf has the following:

       

           public function initApp():void {
              Security.loadPolicyFile("http://mybucket.s3.amazonaws.com/crossdomain.xml" );
            }  

       

           public function loadFont():void{
              var loader:Loader = new Loader();
              var req:URLRequest = new URLRequest();
              req.url = 'http://mybucket.s3.amazonaws.com/some_dir/font.swf;
              loader.load( req );
              loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onFontLoaded );
            }


           public function onFontLoaded( event:Event ):void {
              var dom:ApplicationDomain = event.target.applicationDomain;
              var FontAsset:* = dom.getDefinition('FontAsset') as Class;

              trace( FontAsset.getName() );
              // Exception happens when calling this line
              Font.registerFont( FontAsset.getFont() );
            }

       

      My font.swf file is:

       

                public class FontAsset extends Sprite
                {
                  Security.allowDomain("foo.mydomain.com");

       

                  [Embed(source="#{source_path}", mimeType="application/x-font", fontName='#{font_name}')]
                  private static var font:Class;
                  Font.registerFont( font );
                 
                  public static function getFont():Class{
                    return font;
                  }
                 
                  public static function getName():String {
                    return '#{font_name}';
                  }
                }

       

      I have tried both with and without a constructor and making the font variable non static and then instantiating in main.swf, but I still get the same result. I have also attempted to retrieve the remote font.swf while running my main.swf directly in the browser (file:///home/ruy/main.swf) as well as through a web server.

       

      I found a posting with the exact same behaviour, but the thread that they send me towards seems to address a different issue from the crossdomain problem (http://www.actionscript.org/forums/showthread.php3?t=182499)

       

      The only solution I can find so far is to put my main.swf on the same Amazon S3 server, but it feels like I should be able to host it separately.

       

      Any ideas?

       

      Thanks

      Ruy

        • 1. Re: Loading a font at runtime from a different domain
          Flex harUI Adobe Employee

          Using '*" in a crossdomain.xml is equivalent to leaving the keys in your car and the door unlocked.  Anybody can now get in and use it.  Good for app development and some scenarios, but not always for deployment.  Specify which domains you want to allow.

           

          I would first try adding an allowDomain call in your app that allows the Amazon url.

           

          You might also need to specify a LoaderContext in your load() call to import load the font SWF into the same SecurityDomain

           

          Alex Harui

          Flex SDK Developer

          Adobe Systems Inc.

          Blog: http://blogs.adobe.com/aharui

          1 person found this helpful
          • 2. Re: Loading a font at runtime from a different domain
            diazruy Level 1

            Hi,

             

            Thanks for the reply and sorry for the long delay to get back, but we just

            moved offices and I haven't been able to settle in until today.

             

            At any rate, I'm aware of the security hole of having the * in the

            crossdomain.xml, but for now I just wanted to eliminate the possibility of

            DNS issues getting in the way. I have also added localhost as an allowed

            domain as well as the internal DNS name I am using for my machine.

             

            <?xml version="1.0"?>

            <!DOCTYPE cross-domain-policy

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

            <cross-domain-policy>

                <allow-access-from domain="test.internaldomain.com" />

                <allow-access-from domain="localhost" />

                <allow-access-from domain="*" />

            </cross-domain-policy>

             

            I have also added to my main file:

             

                    Security.allowDomain( "mybucket.s3.amazonaws.com" );

             

            as well as putting a crossdomain.xml in the same directory as the file I am

            retrieving:

             

                    Security.loadPolicyFile("

            http://mybucket.s3.amazonaws.com/some_dir/crossdomain.xml" );

             

            I also added the following for the LoaderContext

             

                    var context:LoaderContext = new LoaderContext( true, new

            ApplicationDomain( ApplicationDomain.currentDomain ) );

                    loader.load( req, context );

             

            Any other suggestions?

             

            Thanks

            Ruy

            • 3. Re: Loading a font at runtime from a different domain
              Flex harUI Adobe Employee

              var context:LoaderContext = new LoaderContext( true, new

              ApplicationDomain( ApplicationDomain.currentDomain ), SecurityDomain.currentDomain );

                      loader.load( req, context );

               

              Alex Harui

              Flex SDK Developer

              Adobe Systems Inc.

              Blog: http://blogs.adobe.com/aharui

              • 4. Re: Loading a font at runtime from a different domain
                diazruy Level 1

                Ok, so I went back and started removing all of the 'fixes' I made trying to figure out the solution in order to identify what precisely was required. I was somewhat surprised to see that the crossdomain.xml and the Security.allowDomain calls are not required, which now takes me to a potential security flaw since anybody can apparently use my assets on S3 (unless I use S3 security on the the folder which I was hoping I would not require). I tried adding a crossdomain.xml file in the root folder to a different domain than the one I am actually making the requests from ( I removed the allow="*" too ) and I am still being able to use the content of the files with no SecurityExceptions being thrown.

                 

                Is this correct behavior or am I missing something else now?

                 

                Ruy

                • 5. Re: Loading a font at runtime from a different domain
                  diazruy Level 1

                  WOOT! That did it! Thanks a ton.

                   

                  Ruy

                  • 6. Re: Loading a font at runtime from a different domain
                    diazruy Level 1

                    Ok, I think everything is working properly now, and since my previous two messages are out of order, I thought I'd clarify how the whole thing ended up just in case someone runs into this again:

                     

                    1. My crossdomain.xml file was being "ignored" because I did not have a meta-policy in it.
                    2. In order to get rid of the fontRegister error, you need to pass SecurityDomain.currentDomain to the LoaderContext.

                     

                    The final setup I ended up with is as follos:

                     

                    Crossdomain.xml located in the root directory of my S3 bucket (http://mybucket.s3.amazonaws.com/crossdomain.xml)

                     

                    <?xml version="1.0"?>
                    <!DOCTYPE cross-domain-policy
                    SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
                    <cross-domain-policy>
                        <site-control permitted-cross-domain-policies="master-only"/>
                        <allow-access-from domain="*.mydomain.com" />
                    </cross-domain-policy>

                     

                    Font asset located on S3 (http://mybucket.s3.amazonaws.com/some_dir/font.swf)

                     

                            package
                            {
                              import flash.display.Sprite;
                              import flash.text.Font;
                             
                              public class FontAsset extends Sprite
                              {


                                [Embed(source="/usr/share/fonts/carbon.ttf", mimeType="application/x-font", fontName='Carbon')]
                                public static var font:Class;
                               
                                public static function getFont():Class{
                                  return font;
                                }
                               
                                public static function getName():String {
                                  return 'Carbon';
                                }
                              }
                            }

                     

                    Main file on a local server (http://somehost.mydomain.com/main.swf)

                    <?xml version="1.0" encoding="utf-8"?>
                    <mx:Application
                      xmlns:mx="http://www.adobe.com/2006/mxml"
                      layout="vertical"
                      >
                      <mx:Script>
                        <![CDATA[
                          import mx.controls.Text;


                          public function loadFont():void{
                            var loader:Loader = new Loader();
                            var req:URLRequest = new URLRequest();
                            req.url = fontUrl.text;
                            var context:LoaderContext = new LoaderContext( true, new ApplicationDomain( ApplicationDomain.currentDomain ), SecurityDomain.currentDomain );
                            loader.load( req, context );
                            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onFontLoaded );
                          }
                       

                     

                          public function onFontLoaded( event:Event ):void {
                            var dom:ApplicationDomain = event.target.applicationDomain;
                            var FontAsset:* = dom.getDefinition('FontAsset') as Class;
                            Font.registerFont( FontAsset.getFont() );
                            var tf:Text = new Text();
                            tf.text = "This is a test "+FontAsset.getName() ;
                            tf.setStyle( 'fontSize', 40 );
                            tf.setStyle( 'fontFamily', FontAsset.getName() );
                            addChild( tf );
                          }
                        
                        ]]>
                      </mx:Script>

                    <mx:TextInput id="fontUrl" text="http://mybucket.s3.amazonaws.com/some_dir/font.swf" />
                      <mx:Button label="Load" click="loadFont();" />
                    </mx:Application>

                     

                     

                    Many thanks to Alex Harui for the help in figuring this out