Загрузка шрифтов в Runtime
Всем читателям желаю хорошего чтения.
Это первая моя запись в блоге, надеюсь у читателей не возникнет проблем с формой подачи материала.
И так начнём с постановки задачи
- Требуется реализовать динамическую загрузку шрифтов непосредственно во время выполнения программы.
Начал я, как обычно с теории. Погуглив всемирную сеть, нашёл несколько примеров как это делают другие в Runtime.
Привожу ccылки некоторые из них
Загрузка ttf - не работает с otf и некоторыми ttf
Загрузка через внешний swf - смутило то, что нужно запоминать названия каждого из шрифтов. Это муторно - если учесть что шрифтов может быть очень много.
После изучения теории, пришел к выводу что для меня, всё же лучше грузить шрифт из swf и перешёл к практике.
Первым с чем я столкнулся, это то - что для получения шрифта, нужно опять таки знать в каком пакете был произведён импорт шрифтов и под какими переменными они объявлены.
Принял решение использовать публичный геттер для этого дела. Во избежания конфликтов при обмене данными между двумя swf использовался общий интерфейс IFontEmbed. ( Кстати единственный найденный мной случай когда без интерфейса не обойтись ( за исключением костылей) )
Сам интерфейс говорит о том что класс который его реализовывает имеет метод который возвращает вектор (плотный массив) классов.
IFontEmbed.as
package me.inpictures.data { public interface IFontEmbed { function getAllFontClass():Vector.<Class> } }
MyriadPro.as
package { import flash.text.Font; import flash.display.Sprite; import flash.system.Security; import me.inpictures.data.IFontEmbed; public class MyriadPro extends Sprite implements IFontEmbed { /* REGULAR */ [Embed(source="../lib/MyriadPro-Regular.otf" ,fontFamily= 'Myriad Pro' ,fontStyle = 'normal' ,fontWeight = 'normal' ,mimeType="application/x-font" ,embedAsCFF = 'false' )] public static const Myriad2Pro:Class; /* BOLD */ [Embed(source="../lib/MyriadPro-Bold.otf" ,fontFamily= 'Myriad Pro' ,fontStyle = 'normal' ,fontWeight = 'bold' ,mimeType="application/x-font" ,embedAsCFF = 'false' )] public static const Myriad2ProBold:Class; /* ITALIC */ [Embed(source="../lib/MyriadPro-It.otf" ,fontFamily='Myriad Pro' ,fontStyle = 'italic' ,fontWeight = 'normal' ,mimeType="application/x-font" ,embedAsCFF = 'false' )] public static const Myriad2ProIT:Class;/*!!!*/ /* BOLD + ITALIC */ [Embed(source="../lib/MyriadPro-BoldIt.otf" ,fontFamily= 'Myriad Pro' ,fontStyle = 'italic' ,fontWeight = 'bold' ,mimeType= 'application/x-font' ,embedAsCFF = 'false' )] public static const Myriad2ProBoldIT:Class; /* CONDENSED */ [Embed(source="../lib/MyriadPro-Cond.otf" ,fontFamily="Myriad Pro Cond" ,fontStyle = 'normal' ,fontWeight = 'normal' ,mimeType="application/x-font" ,embedAsCFF = 'false' )] public static const Myriad2ProC:Class; /* CONDENSED BOLD */ [Embed(source="../lib/MyriadPro-BoldCond.otf" ,fontFamily= 'Myriad Pro Cond' ,fontStyle = 'normal' ,fontWeight = 'bold' ,mimeType="application/x-font" ,embedAsCFF = 'false' )] public static const Myriad2ProCondBold:Class; /* CONDENSED ITALIC */ [Embed(source="../lib/MyriadPro-CondIt.otf" ,fontFamily='Myriad Pro Cond' ,fontStyle = 'italic' ,fontWeight = 'normal' ,mimeType="application/x-font" ,embedAsCFF = 'false' )] public static const Myriad2ProCondIT:Class;/*!!!*/ /* CONDENSED BOLD + ITALIC */ [Embed(source="../lib/MyriadPro-BoldCondIt.otf" ,fontFamily= 'Myriad Pro Cond' ,fontStyle = 'italic' ,fontWeight = 'bold' ,mimeType= 'application/x-font' ,embedAsCFF = 'false' )] public static const Myriad2ProCondBoldIT:Class; public function MyriadPro() { // настройки доступа Security.allowDomain("*"); } // собственно публичный метод который вернёт нам шрифты public function getAllFontClass():Vector.<Class> { var autoset:Vector.<Class> = new Vector.<Class>; autoset.push( Myriad2Pro ); autoset.push( Myriad2ProBold ); autoset.push( Myriad2ProIT ); autoset.push( Myriad2ProBoldIT ); autoset.push( Myriad2ProC ); autoset.push( Myriad2ProCondBold ); autoset.push( Myriad2ProCondIT ); autoset.push( Myriad2ProCondBoldIT ); return autoset; } } }
Теперь перейдем к загрузке шрифтов.
Загрузчик DynamicFont реализовывает интерфейс IFont
IFont.as
package me.inpictures.data { import flash.text.TextField; public interface IFont { function embed(tf:TextField):void } }
(реализация метода embed еще на этапе разработке)
DynamicFont.as
package me.inpictures.data { import flash.events.EventDispatcher; import flash.events.Event; import flash.text.Font; import flash.text.TextField; import flash.text.TextFormat; import flash.display.DisplayObjectContainer; public class DynamicFont extends EventDispatcher implements IFont { private static var _instance:IFont; public function DynamicFont() { // singelton if(_instance) throw new Error("Вы не можете создавать экземпляры класса при помощи конструктора. Для доступа к экземпляру используйте DynamicFont.instance."); } // старт загрузки public function addFont(url:String):void { // создаем экземляр FontLoader'a new FontLoader(url).addEventListener(Event.COMPLETE, autosetter ); } // окончание загрузки FontLoader'a, получаем массив шрифтов и регистрируем их private function autosetter(e:Event):void { e.target.removeEventListener(Event.COMPLETE, autosetter ); var lib:Vector.<Class> = (e.target as FontLoader).lib; for each( var font:Class in lib) Font.registerFont( font ); traceFontRegister(); } // метод для получения единого экземпляра DynamicFont public static function get instance():IFont { if (!_instance) _instance = new DynamicFont(); return _instance; } // собственно метод для подстановки шрифтов в TextField // находиться в стадии разработки public function embed(txt:TextField):void { /* var doc:DisplayObjectContainer = txt.parent; var tf:TextFormat = txt.defaultTextFormat; doc.removeChild( txt ); var params:Array = new Array('x','y','width','height', 'name','text'); var newT:TextField = Object(_loader.content).factory(tf); for each( var prop:String in params) newT[prop] = txt[prop]; doc.addChild( newT ); */ } // Вывод зарегистрированных шрифтов private function traceFontRegister():void { var _arr :Array = Font.enumerateFonts(); for each( var i:Font in _arr){ trace(i.fontName); } } } } import flash.display.Loader; import flash.net.URLRequest; import flash.net.URLLoader; import flash.net.URLLoaderDataFormat; import flash.events.Event; import flash.events.EventDispatcher; import flash.events.IOErrorEvent; import flash.events.SecurityErrorEvent; import me.inpictures.data.IFontEmbed; // Загрузчик шрифтов internal class FontLoader extends EventDispatcher{ private var _url:String; public var lib:Vector.<Class>; public function FontLoader(url:String){ _url = url; // При использовании Loader 'а шрифты не хотели подгружаться, скорей всего из-за политик безопасности // исправилось бинарной загрузкой через URLLoader var byteLoader:URLLoader = new URLLoader(); byteLoader.dataFormat = URLLoaderDataFormat.BINARY; byteLoader.addEventListener(IOErrorEvent.IO_ERROR, errorHandler ); byteLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, errorHandler ); byteLoader.addEventListener(Event.COMPLETE, loadBytesComplete ); byteLoader.load(new URLRequest(_url)); } private function errorHandler(event:Event):void { trace('!!! Загрузка прервана. Адресс загрузки файла: '+_url); switch (event.type) { case 'ioError': trace(' > Файл необнаружен или не доступен.'); break case 'securityError': trace(' > Доступ к файлу запрещён политиками безопастности.'); break } } // по окончании загрузки перегружаем байты в обычный Loader для инициализации private function loadBytesComplete(event:Event):void { event.target.removeEventListener( Event.COMPLETE, loadBytesComplete ); var fontLoader:Loader = new Loader(); fontLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadFontComplete); fontLoader.loadBytes( event.target.data ); } // окончание загрузки private function loadFontComplete(event:Event):void { event.target.removeEventListener(Event.COMPLETE, loadFontComplete); // валидируем swf на интерфейс if (!event.target.content is IFontEmbed) throw new Error("Загружаемый файл не имплементирует IFontEmbed \n url:"+_url); // получаем массив шрифтов, сохраняем как публичное свойство и диспатчим событие окончании загрузки lib = (event.target.content as IFontEmbed).getAllFontClass(); dispatchEvent(new Event(Event.COMPLETE)); } }
Теперь для того чтобы загрузить и инициализировать шрифт достаточно
var dfont:DynamicFont = DynamicFont.instance as DynamicFont; dfont.addFont("MyriadPro.swf");
Очень хочу послушать Ваши размышления, а особенно критику по данной теме и чем она будет жестче тем лучше.
Надеюсь моя статья будет полезна читателям, а кому-то может и вовсе пригодиться как практическое решение.
p.s. Хочу публично поблагодарить Wolsh, который помогал мне практически на всех этапах разработки и сам того не зная натолкнул меня на написание этой статьи.
UPD: реализовал метод для определения стиля у текстового поля и соответственно само переопределение TextFormat у TextField'a
public function embed(txt:TextField):void { var doc:DisplayObjectContainer = txt.parent; doc.removeChild( txt ); var style:TextFormat = getStyles(txt); var newT:TextField = new TextField(); newT.setTextFormat( style ); newT.defaultTextFormat = style; var params:Array = new Array('x','y','width','height', 'name','text','type'); for each( var prop:String in params) newT[prop] = txt[prop]; newT.embedFonts = true; newT.text = txt.text; doc.addChild( newT ); } private function getStyles(tf:TextField):TextFormat { var htmlTag:String = tf.htmlText; var fontStyle:TextFormat = tf.defaultTextFormat; var params:Array = new Array('FACE','SIZE','COLOR','LETTERSPACING','KERNING'); var retype:Object = new Object(); retype.FACE = 'font'; retype.SIZE = 'size'; retype.COLOR = 'color'; retype.LETTERSPACING = 'letterSpacing'; retype.KERNING = 'kerning'; var r:Object; for each( var key:String in params) { r = searchEngine(htmlTag,key); fontStyle[retype[key]] = r.value; htmlTag = r.str; } fontStyle.bold = htmlTag.search('<B>')!=-1; fontStyle.italic = htmlTag.search('<I>')!=-1; return fontStyle; } private function searchEngine(str:String,find:String):Object { var res:Object = new Object(); var pos:Number = str.indexOf(find)+find.length+2; var end:Number = str.indexOf('"', pos+1); res.value = str.substring( pos, end ); res.str = str.substring( end, str.length ); return res; }
Пример записи в TextField который был создан на сцене с указанием семейства шрифта, начертания, но без непосредственного импорта.
Код:
<TEXTFORMAT LEADING="2"><P ALIGN="LEFT"><FONT FACE="Myriad Pro Cond" SIZE="24" COLOR="#FFFFFF" LETTERSPACING="0" KERNING="1"><I>василий петрович</I></FONT></P></TEXTFORMAT>
1. При импортировании шрифта в отдельный swf, fontFamily должен совпадать с тем как его видит сам флеш-плеер. (если конечно не хотите писать ассоциации Ваших имён шрифтов)
2. Нет методов обеспечивающие разбор styleSheel для создания "мульти"-шрифтовых полей с различным типом начертания.
Всего комментариев 2
Комментарии
18.10.2013 19:37 | |
Ваш метод лишь экономит на изначальном весе флешки не включая в неё шрифты, но вроде не даёт загрузить любой нужный шрифт в рантайме, как тут (хоть и не работает с otf) - вся соль в том что тут он програмно создаёт этот свф со шрифтом.
Т.е. чтобы дать юзеру воможность выбрать из большого списка нужно каждый шрифт загонять в свф по вашему примеру мириад про, что есть не очень удобно. Я к тому что не стоит сравнивать ваш подход и тот описанный в ссылке выше. |
18.10.2013 21:31 | |
Цитата:
чтобы дать юзеру воможность выбрать из большого списка нужно каждый шрифт загонять в свф
С точки зрения конечного пользователя, да ему не предоставляется интерфейс для загрузки своего шрифта, но с точки зрения разработчика появляется инструмент для наполнения библиотеки шрифтов. В моем же проекте всё разбито на отдельные блочные swf (страницы так сказать). И всё это имеет дополнительно вариацию отображений (т.е. скины). Скины в свою очередь Цитата:
Я к тому что не стоит сравнивать ваш подход и тот описанный в ссылке выше.
Конечно хотелось бы разобраться как там всё устроенно, чтобы попытаться решить вопрос с поддержкой всех типов шрифтов, но я не располагаю, пока что таким количеством времени, потому решил вопрос так как описано выше. p.s. конвертировав otf в ttf, для использования его с помощью этого, не давало результатов, в моем проекте первым что нужно использовать Myriad Pro, которого в формате ttf я лично не нашёл, а сидеть у разбитого корыта я не привык, потому собственно и Вы смотрите данную реализацию. На сколько она оправдана, это еще предстоит выяснить, не исключено что всё же есть другие методы решения данного вопроса с полной поддержкой шрифтов, как при "ручном" импорте. |
Последние записи от AlexCooper
- Загрузка шрифтов в Runtime (17.10.2013)