flex动态生成矢量swf字体--java动态生成swf文件
前言 相信很多在线设计的前端WEB应用会用到字体作为素材的案例,丰富多样的字体一般是部署在服务器端让用户去选择,而且能动态部署,使用客户端字体显然是不可取的;
现状
然而中文字体动不动就几个M,做过flash的同学应该知道嵌入字体是怎么回事,假如嵌入字体会使得swf整个体积变大,而flex目前容易做到的是整个字体文件编译成swf格式来动态加载。假如需要根据用户输入的文字动态的加载该如何做呢?
最简单的方案是后台根据字体生产位图,这不能满足我的要求,1,位图拉伸后有锯齿2,大一点的位图文件很占空间,前台设计需要至少1000×1000以上才能保证清晰度,这么一张图动不动就大于100K。
目的
最完美的一个方案,是能让我的用户输入文字后,生成一个对应字体仅仅包含这几个文字的swf资源文件,这个swf占的空间很小并且在flex中用就可以加载。
阿里妈妈bannermaker是有这个方案的,但是商业机密没有公开出来,可以肯定的是一定在后台端生成swf,至于怎么产生,目前也没找到过现成方案。只能自己研究了,经过一段时间实践,结合java动态生成swf的一个框架,终于成功了,20个文字的矢量图,10K不到,而且矢量图和字体大小无关,随便前端怎么拉伸无锯齿。
首先,重述下动态矢量swf字体是什么含义呢?
用户-》选择1字体并输入'abc'-》后台生成一个swf,这个swf仅仅包含1字体中abc三个字符并且是矢量的。
类似于flash IDE里的这个功能:
嵌入字体并导出资源swf图像。
遗憾的是IDE不容易和应用整合,如何用代码来实现呢?
实现的核心:java登场了,感谢anotherbigidea大师的开放和分享
http://www.anotherbigidea.com/javaswf/
这已经是anotherbigidea大师03年时候的杰作了,用java io操作 根据flash 6字节位组织方式可以从swf 1中提取任何的元素再去动态生成swf 2的内容。
因此,一个想法诞生了,后台放置一个字体a的全量swf1文件,java端获取到用户输入的文字后动态的从swf1拷贝那几个图形文字生成一个 swf2 不就ok了么。 果然,如我所料:
1,全量的swf字库怎么做?
可依照上图,在本地安装字体后,嵌入所有字符导出一个swf,最好是swf 6.0格式。(flex和flash共享资源文件目前都用6)
2,怎么用java动态生成swf?
/**
* 简单测试
*/
package test;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import com.anotherbigidea.flash.movie.Font;
import com.anotherbigidea.flash.movie.FontDefinition;
import com.anotherbigidea.flash.movie.FontLoader;
import com.anotherbigidea.flash.movie.Frame;
import com.anotherbigidea.flash.movie.Movie;
import com.anotherbigidea.flash.movie.Text;
import com.anotherbigidea.flash.movie.Font.NoGlyphExcepti on;
import com.anotherbigidea.flash.structs.Color;
/**
* @author jianwen
* @email [email protected]
*
*/
public class Testtxt {
/**
* @param args
* @throws IOException
* @throws NoGlyphException
*/
public static void main(String[] args) throws IOException, NoGlyphException {
//--Load a font from another movie. That movie should contain only an
// edit field that is specified to include all the glyphs in the
// appropriate font.
// Font definitions can be referenced by multiple fonts (for example if
// there is a font used for a text block and another font used with an
// edit field which restricts the allowable characters).
//初始化 加载全量字库
java.awt.Font nf = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FO NT, new FileInputStream("c://mnjmb.ttf"));
//第一步,输入文字
String instr = "文字这个的房贷首付第三方第三方\n\n\n地方地方haha牛x第三方我祖国好地方\n地方\ndfdfdsf";
//第二步:选择/导入字库
/////////避免重复加载
String fontpath = "ccxkt";
HashMap fonts = new HashMap();
FontDefinition fontdef = fonts.get(fontpath);
System.out.println("当前使用内存:"+(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/1024/1024);
if(fontdef==null){
//导入swf格式的文字类库,注意路径
fontdef = FontLoader.loadFont( "c://"+fontpath+".swf" ); //导入全量的字库,我这里有很多个,为了测试性能,发现一个中文字体要占30M左右的内存
fonts.put(fontpath, fontdef);
}
// File f = new File("c://ch");
// System.out.println("一共"+f.list().length+"个中文字体");
// for (String s :f.list()){
// System.out.print(s);
// fonts.put(s,FontLoader.loadFont("c://ch//"+s));
// System.out.println(":"+(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/1024/1024);
// }
// System.out.println("加载中文字体后内存2:"+(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/1024/1024);
// f = new File("c://en");
// System.out.println("一共"+f.list().length+"个英文字体");
// for (String s :f.list()){
// System.out.print(s);
// fonts.put(s,FontLoader.loadFont("c://en//"+s));
// System.out.println(":"+(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/1024/1024);
// }
// System.out.println("加载所有字体后内存3:"+(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/1024/1024);
/////下面是动态生成文字对应的swf部分,性能很不错,io操作由于是内存拷贝,奇快无比,比后台动态用flex sdk编译方式还要快
//初始化参数
int fontsize = 100;//默认字体大小,由于矢量格式,大小没关系(swf文件大小不受此参数影响)
Color c = new Color(0,0,0);//文字颜色
//--Create a text object with a default transform
Text text = new Text(null);
//--The font references the Font Definition and pulls over only the
// glyph definitions that are required for any text referencing the font
System.out.println("len1:"+fontdef.getGlyphList(). size());
Font font = new Font( fontdef );
//第四步:
/////处理输入字符串,支持回车多行解析
StringReader re = new StringReader(instr);
BufferedReader bf = new BufferedReader(re);
String s = bf.readLine();
int i=0;
while(s!=null){
//多行间必须使用空格
if(s.length()==0){s = " ";}
//--Add a row of characters - specify the starting (x,y) within the text symbol
try {
text.addRow(font.chars(s, fontsize), c, 0, fontsize * i + 5,true, true);
} catch (NoGlyphException e) {
// 某文字没有该对应字体,忽略
}
s = bf.readLine();
i++;
}
/////解析完毕
//--Add another row - different color, no (x,y) specified so the chars will
// flow immediately after the preceding chars.
// text.row( font.chars( "2我来无敌", 25), new Color(255,0,255),0,0,false,false);
//计算文本框大小,重要,必须在movie初始化前 这是我改造的代码,先计算大小
text.measureRect(null, null, font, false);
//默认文字x=0, 推算出中文宽度需要多加一个单位的fontsize,偶也不知道为什么,加了没坏处
Movie movie = new Movie( (int)text.maxX+fontsize,(int)(text.maxY-text.minY) ,12,5,null);
Frame frame = movie.appendFrame();
//--instantiate the text
frame.placeSymbol( text, 0, -((int) text.minY ));//让文本框显示在0,0坐标
//--save the movie
movie.write( "c://output.swf",true );
//输出到流
//movie.write( OutputStream out, boolean compressed )
}
}
最后,大师提供了一个基础,优化需继续
这种方式生成的swf比通过flexsdk动态编译生成swf要快而且文件要小,精简,很多无用信息都不会生成。
不过也有几个局限:
1,字体swf必须导出flash 6格式, 只能提供了简单的动画接口。
2,后台导入的是一个全量swf库,占用静态内存极大,要部署50套字体的话 2G内存都不够;动态加载全量字体的话也是不可行的,因为加载一个全量字体花费极大,可考虑拆分部署,或者按字体文字的字节流引入数据库,再或者可按字体分布多个服务器部署。
这个库已经很久没人更新了,里面的代码逻辑看得一头雾水,虽然功能和性能都通过了,但是基于继续大师的优化工作还有待继续前行。
参考文献:
http://www.anotherbigidea.com/javaswf/
猜你喜欢
转载自hgfghww6.iteye.com/blog/1572322
今日推荐
周排行