embedding a font in a swf using as3 - actionscript-3

I have a project using flash (and AIR for android and eventuall iOS) for translating some phrases. Although most fonts are fine there are some that I have to load after the user has chosen the languages. Over a year ago I generated a couple of swf files (Bengali and Urdu) and put them on my web server. The flash application then loads them when required and everything is OK...
However as I am nearing implementing the project I thought I should generate the swf files for the other languages that are in the pipeline and for some reason I can't manage it! Needless to say I have misplaced (tidied) the original AS3 source for the two fonts I have done.
package
{
import flash.display.Sprite;
import flash.events.Event;
public class ArabicF extends Sprite
{
[Embed(source = "trado.ttf",
fontName = "ArabicX",
fontFamily = "ArabicY",
fontWeight = "normal",
fontStyle = "normal",
mimeType = "application/x-font",
advancedAntiAliasing="true",
embedAsCFF="true")]
public static const ArabicZ:Class;
}
}
This has expanded from a much simpler form as I added things to get it to work and I must have tried all permutations of true and false. I load the swf ok but then can't extract the class. With the two fonts I embedded before the swf class and the parameter all have the same name and they work fine (i.e.
Urdu.swf
then later:
var FontClass:Class = evt.target.applicationDomain.getDefinition("Urdu") as Class;
and then
Font.registerFont(FontClass.Urdu);
in the calling .as application with the above Arabic.swf I try
trace("1 Arabic "+evt.target.applicationDomain.hasDefinition("ArabicF"));
trace("2 Arabic "+evt.target.applicationDomain.hasDefinition("Arabic"));
trace("3 ArabicX "+evt.target.applicationDomain.hasDefinition("ArabicX"));
trace("4 ArabicY "+evt.target.applicationDomain.hasDefinition("ArabicY"));
trace("5 ArabicZ "+evt.target.applicationDomain.hasDefinition("ArabicZ"));
but all return false
PS also tried generating the Arabic.swf using fontswf.bat which again seems to make a very similar swf file which is loaded but I can't extract the class from it
PPS I'm using flashdevelop and the font swf are set up as AS3 standard project compiler options.

I don't use Flash Develop, but perhaps I can offer some perspective from my implementation.
If I wanted to embed Arial, the embedding swf would "Export for ActionScript" the embedded font. In its document class, use registerFont() as below:
Font.registerFont(Arial);
Like every other swf, you'd use a Loader to import it into your runtime, and at that point, the font will be available to your global table. You can query the complete list of registered fonts using enumerateFonts().
var fontList:Array = Font.enumerateFonts();
for (var i:int = 0; i < fontList.length; i++) {
trace(fontList[i].fontName);
}
Assuming we've got a TextField called "txt", you can then implement that font with setTextFormat() like so:
var format:TextFormat = new TextFormat();
format.font = "Arial";
txt.embedFonts = true;
txt.antiAliasType = AntiAliasType.ADVANCED;
txt.setTextFormat(format);
txt.defaultTextFormat = format;
A disclaimer from Adobe on the use of these last two settings:
"When you apply a TextFormat object to a text field using the
TextField.defaultTextFormat property or the TextField.setTextFormat()
method, only its defined properties are applied. Use the
TextField.defaultTextFormat property to apply formatting BEFORE you
add text to the TextField, and the setTextFormat() method to add
formatting AFTER you add text to the TextField."
As I said before, my workflow doesn't utilize Flash Develop, but rather Flash IDE to embed the fonts into the swfs. Your results may vary, but be aware you must set embedFonts to true, or it won't respond to your embedded font. I've lost hair on that issue before.
Hope that helps. Cheers,

OK In case someone else runs into similar problems this is stage one of the answer (really the answer to my original question) still have to sort out how to get the font to be used in the flash.font.engine apparatus!
The critical parts are:
Point #1. the embedded font swf must have a name that doesn't clash with ANY name registered in the calling script. Bizzare and (as far as I could search) undocumented. i.e. I have a list of languages uploaded via xml services to an xml object and that includes for instance 'English' however the font loading doesn't work until I make the loadable font file into 'EnglishF.swf' The obvious choice of just 'English.swf' fails. So my embedded font source is now called EnglishF.swf and reads:
package
{
import flash.display.Sprite;
import flash.text.Font;
import flash.system.Security;
Security.allowDomain("*");
/**
* ...
* #author patrick
*/
public class EnglishF extends Sprite
{
[Embed(source = "Kingthings Exeter.ttf",
fontName = "EnglishF",
fontFamily = "EnglishF",
fontWeight = "normal",
fontStyle = "normal",
mimeType = "application/x-font",
advancedAntiAliasing="true",
embedAsCFF="true")]
public static var myFont:Class;
}
}
and the using script goes:
private function fontLoaded(evt:Event):void {
var FontClass:Class
if (evt.target.applicationDomain.hasDefinition(font1Name)) FontClass = evt.target.applicationDomain.getDefinition(font1Name) as Class;
else if (evt.target.applicationDomain.hasDefinition(font2Name)) FontClass = evt.target.applicationDomain.getDefinition(font2Name) as Class;
try {
Font.registerFont(FontClass.myFont);
trace("successfully loaded " + FontClass);
} catch (err:Error) {
trace("couldn't register font "+FontClass.myFont+" =>"+err);
}
}
The line in the embedded script about security is because of point #2: the registerFont doesn't work across domains. In fact the Security.allowDomain() didn't work and to debug this I have had to copy/paste the swf files on the PC and change the loader.load(URLRequest()) accordingly
There is still an issue that although the font is registered and claims to be compatible with the flash.text.engine TextElement requirements it still fails to be used as in:
_tl = new Array();
var block:TextBlock = new TextBlock();
var font:FontDescription = new FontDescription(fontName);
var formt:ElementFormat = new ElementFormat(font, fontSize);
//following gives 'true EnglishF'
trace(FontDescription.isFontCompatible(fontName,"normal","normal"), formt.fontDescription.fontName);
formt.color = colr;
var span:TextElement = new TextElement(text, formt);
block.content = span;
_tl[0] = null;
_tl[0] = block.createTextLine(null, width);
var tl:TextLine;
// after here justification and addition lines
I will post something if and when I sort out why this doesn't work (Unless some kind person does it for me!)

Related

Flash loading first external swf loaded

I am making an application to test art from a game I volunteered for. Right now the example I am posting will only touch the armors but the loading process is the same throughout the program. I have a movieclip ready to hold the loaded file but it adds it to the container via the class. It works how it should however my issue is that if you use another file with the same classes then it will default to the first file loaded. Even i use loaderr.unloadAndStop() and remove everything from the stage, it will always load the first file that corresponds to the class I am loading by. Since the armor pieces are loaded by class it makes it a hassle to test multiple changes to an armor file without changing the classes on each export. Here is an example of the code that is being used and I am curious if there is any way that I can improve this. `
public class Test extends MovieClip
{
public var mcChar:Display;
public var btnTest:SimpleButton;
public var btnTest2:SimpleButton;
public var ldr:Loader = new Loader();
public var strSkinLinkage:String;
public var strGender:String;
public function Test()
{
btnTest.addEventListener(MouseEvent.CLICK, TestP);
btnTest2.addEventListener(MouseEvent.CLICK, TestP2);
}
public function TestP(e:MouseEvent)
{
mcChar = new Display();
stage.addChild(mcChar);
mcChar.x = 789.6;
mcChar.y = 604.75;
mcChar.width = 667.15;
mcChar.height = 478.55;
strSkinLinkage = "CNC";
strGender = "M"
this.ldr.load(new URLRequest("CNC.SWF"), new LoaderContext(false, ApplicationDomain.currentDomain));
this.ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, this.onLoadSkinComplete);
}
public function TestP2(e:MouseEvent)
{
mcChar = new Display();
stage.addChild(mcChar);
mcChar.x = 789.6;
mcChar.y = 604.75;
mcChar.width = 667.15;
mcChar.height = 478.55;
strSkinLinkage = "CNC";
strGender = "M"
this.ldr.load(new URLRequest("CNC2.SWF"), new LoaderContext(false, ApplicationDomain.currentDomain));
this.ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, this.onLoadSkinComplete);
}
public function onLoadSkinComplete(e:Event):*
{
var AssetClass:Class;
try
{
AssetClass = (getDefinitionByName(((strSkinLinkage + strGender) + "Head")) as Class);
mcChar.head.addChildAt(new (AssetClass)(), 0);
}
catch(err:Error)
{
AssetClass = (getDefinitionByName(("mcHead" + strGender)) as Class);
mcChar.head.addChildAt(new (AssetClass)(), 0);
};
AssetClass = (getDefinitionByName(((strSkinLinkage + strGender) + "Chest")) as Class);
chest.addChild(ldr.content (AssetClass)());
mcChar.chest.addChild(new (chest)());
this.ldr.contentLoaderInfo.removeEventListener(Event.COMPLETE, this.onLoadSkinComplete);
}
}
`
I don't think its well formatted on this site but this is the core code. I have separate removal functions and my imports are all there. Like I said I cant seem to get it to format correctly. This is my test scenario and isn't my full dynamic tester where I can choose the file. Any help in figuring out how to use the most recent file is appreciated. Also for some background I am more of a self taught novice in as3.
When it gets to loading and unloading assets in AS3, there are several things to learn.
ApplicationDomain is a container for class definitions. The getDefinitionByName(...) method is basically the same as calling the ApplicationDomain.getDefinition(...) on the current ApplicationDomain (or maybe on the main ApplicationDomain, I never tried to do it in the loaded content). As the side result, you cannot have two classes with the same names inside the same ApplicationDomain (or rather you can, but one of them is inaccessible, who knows).
When you load another SWF which falls into the "same domain" category (same www domain, or same/nested local folder), AS3 automatically mixes all the definitions from the loaded SWF into the main ApplicationDomain. If you are willing to have some advanced control over loading/unloading stuff, or/and there are "skin" libraries that have similar sets of classes, you need to put the loaded files into separate ApplicationDomains or their definitions will collide and the result will be unpredictable (yet obviously not satisfactory).
The Loader.load(...) method has a second argument that allows you to do so:
// If there are no mandatory constructor arguments,
// you are free to omit the () brackets. I like doing so.
var aLoader:Loader = new Loader;
var aRequest:URLRequest = new URLRequest("mylibrary.swf");
// Documentation states that passing no argument here is
// the same as passing ApplicationDomain.currentDomain.
var childDomain:ApplicationDomain = new ApplicationDomain;
var aContext:LoaderContext = new LoaderContext(false, childDomain);
aLoader.load(aRequest, aContext);
Thus, when external SWF library is loaded, you can obtain its classes/definitions as following:
var aClass:Class;
// If you get things from the loaded SWF's Library
// then it is Sprite or MovieClip for the most cases.
var anAsset:Sprite;
aClass = aLoader.contentLoaderInfo.applicationDomain.getDefinition("MyAssetClass") as Class;
anAsset = new aClass;
When you do not longer need some of the loaded libraries, you call the Loader.unloadAndStop(...) method on the relevant Loader instance. Combined with the loading SWF into separate ApplicationDomain you can be sure that all of the loaded content (graphics, classes, sounds) is unloaded, destroyed and removed (that one I actually checked):
// Passing "true" also forces the Garbage Collector
// to actually do its job for a change.
aLoader.unloadAndStop(true);

How to use a font embedded in a SWC

I exported a SWC from Flash CS3 with a font embedded.
Now I want use it in some TextFields, but I don't know how to use the font.
Done! Here's my solution:
[Embed(source="res/guardanapo.otf", fontName="guardanapo", fontFamily="guardanapo", unicodeRange="U+0020-U+002F,U+0030-U+0039,U+003A-U+0040,U+0041-U+005A,U+005B-U+0060,U+0061-U+007A,U+007B-U+007E", advancedAntiAliasing="false", embedAsCFF="false")]
public static const fonte:Class;
I wanted to embed the font from SWC, but now it isn't necessary anymore. But answer if you know how to do that, maybe someone is serching for it now.
Give this a try. myLoader is the loader that you loaded your swf/swc with the font in its library. myTextField is a pre-existing text field. I set embedFonts to true, but you may not need it.
var MyFont:Class = myLoader.contentLoaderInfo.applicationDomain.getDefinition("FontClassName");
var embeddedFont:Font = new MyFont();
var textFormat:TextFormat = new TextFormat();
textFormat.font = embeddedFont.fontName;
textFormat.size = 24;
myTextField.setTextFormat(textFormat);
myTextField.embedFonts = true;
Maybe you can include a font-fetching class in the swf with the font(s) you want to use. There could be static methods that apply a font to a field, or return the font name given the font's class name. This could help keep the rest of your application cleaner.

Actionscript 3 - Import SVG file

Does anyone know of any libraries or tools for importing an SVG file into actionscript on the fly? I need to be able to import an SVG file into flash and convert it to a movieclip or preferably a flat 3d object. The end goal here is to implement a vector file with Augmented reality.
A great library for this can be downloaded here: http://code.google.com/p/as3svgrendererlib/
It is easy to implement and reliable. Note that there are two ways to implement the code. I prefer the following because it gives you more control:
private function loadSVG(url:String):void {
ProcessExecutor.instance.initialize(stage);
ProcessExecutor.instance.percentFrameProcessingTime = 0.9;
loader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.TEXT;
loader.addEventListener(Event.COMPLETE, svgLoaded, false, 0, true);
try {
loader.load(new URLRequest(url));
} catch (error:Error) {
trace(error);
}
}
private function svgLoaded(e:Event):void {
var svgString:String = loader.data as String;
var svgDocument:SVGDocument = new SVGDocument();
svgDocument.parse(svgString);
this.addChild(svgDocument);
}
The other way is shorter and might be the way to go when you only have to load a small SVG or just a few.
public function loadSVG(url:String) {
ProcessExecutor.instance.initialize(stage);
ProcessExecutor.instance.percentFrameProcessingTime = 0.9;
var svgDocument:SVGDocument = new SVGDocument();
svgDocument.load(url);
addChild(svgDocument);
}
Two important notes:
1. I could not seem to be able to capture the width or height of the SVGs and the parent Sprite had a width and height of 0 after loading the SVG. I solved this by making all the SVGs the same size before loading them into AS3. After that I knew the dimensions I used the ScaleX and scaleY to resize the loaded SVGs.
2. The stage has to exist before you can use this code. adding the following code will make sure you won't run into problems:yourDisplayClass.addEventListener(Event.ADDED_TO_STAGE, loadYourSVGs);
Now how you convert this to a flat 3D object depends on your 3D Library. I have worked with Away3D where you can use bitmapmaterial on your Sprite3D. The Sprite3D class would be the object to use. I hope your 3D Library supports the use of MovieClips so that you can add them to your 3D Object. Else you will have to use to extract the bitmapMaterial from the movie clip as i have done in the following example:
public function movieClipToBitmapMaterial(mc:MovieClip):BitmapMaterial {
var bmData:BitmapData = new BitmapData(mc.width, mc.height, true, 0xFFFFFF);
bmData.draw(displayObject);
return new BitmapMaterial(bmData);
}
Your input in the above function will be the movieclip onto wich you have loaded your SVG.
I am not sure of this but I think that by using this function you will loose the ability to scale as much as you like without loosing quality.
I hope my input was of some help!
Good luck
PS: don't forget to add the SWC from the link to your project and to check out the provided examples. Please note as well the excellent comment of shaunhusain on your original question. You might not have to use 3D Library.
Salam,
It is very easy:
Open the "name.svg" file in illustartror.
Save it as "name.ai"
Import it into the stage in flash, that is all.

Is it possible to use a non-embedded fallback font when using an embedded font with AS3 TextField?

I have an embedded font in my AIR/AS3 app that lacks support for most international characters. Using TextField and StyleSheet with the font-family property, I assumed I would simply need to do this:
font-family: Interstate-Regular, _sans;
This works if TextField.embedFonts = false; but then Interstate-Regular isn't embedded for users that don't have it on their system. With TextField.embedFonts = true; the text doesn't even show up. Is there a way to embed Interstate-Regular and still use _sans as a fallback system font without embedding it as well?
Flash Text Engine has this "fallback" feature, but it is slower than regular TextField, and more difficult to use it.
Link to the Adobe Manual
You could implement a switch in a custom FontManagement class , if a language is not supported by your main font , revert to a non embedded font. To achieve this , you could use this FontManagement class as a centralized point where to format your TextFields. This could be achieved by creating a public static function which would return a TextField with the relevant format.
//where you need to format a TextField
var params:Object = {color:0xffffff , size:12, supported:false , etc...};
var tf:Texfield = FontManagement.formatTextField(tf , params );
public class FontManagement
{
//A basic example
public static function formatTextField( tf:TextField , params:Object ):TextField
{
//since this is a static function , the Boolean is passed as an argument
//but there are other ways to set it, depending on where in your app
//the language is identified
if( params.supported )
tf.embedFonts = true;
else
tf.embedFonts = false;
//here the rest of your formatting code
return tf;
}
}

How to get all definitions in an ApplicationDomain of a loaded SWF?

When you load a SWF into another, the loader SWF can get specific definitions from the loaded SWF using ApplicationDomain.getDefinition(name:String). For example:
package
{
// ... imports
public class SWFLoader extends Sprite
{
private var loadedAppDomain:ApplicationDomain;
public function SWFLoader()
{
var request:URLRequest = new URLRequest("test.swf");
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onTestLoadComplete);
loader.load(request);
}
private function onTestLoadComplete(event:Event):void
{
var loaderInfo:LoaderInfo = LoaderInfo(event.target);
loadedAppDomain = loaderInfo.applicationDomain;
// Here we can get ANY defined symbol (class, namespace or function according to Adobe Flash help)
var someSymbolClass:Class = Class(loadedAppDomain.getDefinition("SomeSymbol"));
var someSymbolSprite:Sprite = Sprite(new someSymbolClass());
addChild(sprite);
}
}
}
How can I get all of the definitions in a SWF, without specifying each explicitly?
As of Flash Player 11.3, you can use ApplicationDomain.getQualifiedDefinitionNames().
See the official documentation for the method and this blog post about the Flash Player release.
EDIT: This is the quickest solution to your problem : http://www.bytearray.org/?p=175
Hi, you could use this library : https://github.com/claus/as3swf/wiki/
Don't have the time to do deeper test, but here is what i found :
1 - I have created a .swf containing in the library 2 exported MC, $Test and $Test2
2 - Once the .swf loaded by a Loader, i run this code :
var swf : SWF = new SWF(loader.contentLoaderInfo.bytes);
trace(swf);
3 - In the output you'll notice theses lines :
[76:SymbolClass]
Symbols:
[0] TagID: 2, Name: $Test2
[1] TagID: 1, Name: $Test
I think that there is a way to obtain this info directly thru the library API
You have to put the loaded SWF in the current ApplicationDomain.
Use ApplicationDomain.currentDomain to do that, on the ContextLoader info.
loader.load(request, new ContextLoader(false, ApplicationDomain.currentDomain));
It should work.
Following from the answer I received from a previous question I asked here a few days ago (it's about SWC , but in your case, it doesn't really make a difference )
Working with SWCs - getDefinitionByName issue
If both SWFs share the same ApplicationDomain, you should be able to access the loaded SWF classes directly by doing this:
//provided that SomeSymbol extends Sprite...
var someSymbolSprite:Sprite =new SomeSymbol();
On the other hand, you won't be able to do this
var SomeSymbol:Class = getDefinitionByName("SomeSymbol");
unless you create a library of objects from the loaded SWF
var ssym:SomeSymbol;
Check the above link for more details.