Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > AlexCooper

Рейтинг: 5.00. Голосов: 3.

Загрузка шрифтов в Runtime

Запись от AlexCooper размещена 17.10.2013 в 05:42
Обновил(-а) AlexCooper 17.10.2013 в 13:37

Всем читателям желаю хорошего чтения.
Это первая моя запись в блоге, надеюсь у читателей не возникнет проблем с формой подачи материала.

И так начнём с постановки задачи
- Требуется реализовать динамическую загрузку шрифтов непосредственно во время выполнения программы.

Начал я, как обычно с теории. Погуглив всемирную сеть, нашёл несколько примеров как это делают другие в Runtime.
Привожу ccылки некоторые из них
Загрузка ttf - не работает с otf и некоторыми ttf
Загрузка через внешний swf - смутило то, что нужно запоминать названия каждого из шрифтов. Это муторно - если учесть что шрифтов может быть очень много.

После изучения теории, пришел к выводу что для меня, всё же лучше грузить шрифт из swf и перешёл к практике.
Первым с чем я столкнулся, это то - что для получения шрифта, нужно опять таки знать в каком пакете был произведён импорт шрифтов и под какими переменными они объявлены.

Принял решение использовать публичный геттер для этого дела. Во избежания конфликтов при обмене данными между двумя swf использовался общий интерфейс IFontEmbed. ( Кстати единственный найденный мной случай когда без интерфейса не обойтись ( за исключением костылей) )

Сам интерфейс говорит о том что класс который его реализовывает имеет метод который возвращает вектор (плотный массив) классов.

IFontEmbed.as
Код AS3:
package me.inpictures.data {
 
	public interface IFontEmbed {
 
		function getAllFontClass():Vector.<Class>
 
	}
 
}
Теперь создаем пустую флешку, с документ классом MyriadPro который импортирует нужное нам семейство шрифтов и имплементируем интерфейс IFontEmbed

MyriadPro.as
Код AS3:
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;
		}
 
	}
}
Компилируем swf и выгружаем на удалённый хостинг

Теперь перейдем к загрузке шрифтов.
Загрузчик DynamicFont реализовывает интерфейс IFont
IFont.as
Код AS3:
package me.inpictures.data {
 
	import flash.text.TextField;
 
	public interface IFont {
 
		function embed(tf:TextField):void
 
	}
 
}
Публичный метод embed предназначен для того чтобы получить на входе TextField с не embed-шрифтом но прописанным StyleSheet, а вернуть его уже "красивеньким".
(реализация метода embed еще на этапе разработке)

DynamicFont.as
Код AS3:
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));		
	}
 
}
Собственно и всё.

Теперь для того чтобы загрузить и инициализировать шрифт достаточно
Код AS3:
var dfont:DynamicFont = DynamicFont.instance as DynamicFont;
	dfont.addFont("MyriadPro.swf");
Конечно логично класс DynamicFont расширить процессингом этапов загрузки и на это время останавливать основную программу, во избежание попыток импорта еще не загруженных шрифтов, но в своей реализации я поддерживаю мультипроцессинг, потому метод DynamicFont.embed будет делать кросс-проверку и в случаях проблем с шрифтом (загружается,отсутствует) будет сохранять ссылки на текстовые поля и после окончания загрузки переопределять или в случае отсутствии будет стартовать загрузку.

Очень хочу послушать Ваши размышления, а особенно критику по данной теме и чем она будет жестче тем лучше.
Надеюсь моя статья будет полезна читателям, а кому-то может и вовсе пригодиться как практическое решение.

p.s. Хочу публично поблагодарить Wolsh, который помогал мне практически на всех этапах разработки и сам того не зная натолкнул меня на написание этой статьи.

UPD: реализовал метод для определения стиля у текстового поля и соответственно само переопределение TextFormat у TextField'a
Код AS3:
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.htmlText который имеет всё необходимую нам информацию о нужном стиле который следует применить к данному TextField.
Пример записи в 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 AlexLucas вне форума
AlexLucas
 
Аватар для AlexLucas
Ваш метод лишь экономит на изначальном весе флешки не включая в неё шрифты, но вроде не даёт загрузить любой нужный шрифт в рантайме, как тут (хоть и не работает с otf) - вся соль в том что тут он програмно создаёт этот свф со шрифтом.
Т.е. чтобы дать юзеру воможность выбрать из большого списка нужно каждый шрифт загонять в свф по вашему примеру мириад про, что есть не очень удобно.
Я к тому что не стоит сравнивать ваш подход и тот описанный в ссылке выше.
Старый 18.10.2013 21:31 AlexCooper вне форума
AlexCooper
 
Аватар для AlexCooper
Цитата:
чтобы дать юзеру воможность выбрать из большого списка нужно каждый шрифт загонять в свф
AlexLucas это в принципе абсолютно логично. Вариант с загрузкой чистого ttf в рантайм, как писали здесь работал с некоторыми ttf не корректно, а с otf вовсе не дружил. Мне же нужна 100% поддержка шрифтов которые сам же флеш поддерживает, и пусть это и займет на первых этапах дополнительные усилия. Это ведь стандартная по сути процедура за исключением публикации в отдельный swf. Да и сами знаете что можно написать небольшой скрипт на php который сгенерирует нам нужные классы (если шрифтов уж слишком много). Всё что останется это один раз в жизни провести компиляцию каждого из них.
С точки зрения конечного пользователя, да ему не предоставляется интерфейс для загрузки своего шрифта, но с точки зрения разработчика появляется инструмент для наполнения библиотеки шрифтов.
В моем же проекте всё разбито на отдельные блочные swf (страницы так сказать). И всё это имеет дополнительно вариацию отображений (т.е. скины). Скины в свою очередь могут будут иметь свои шрифты, но во-время сессии пользователя, далеко не факт что он будет использовать другие скины.
Цитата:
Я к тому что не стоит сравнивать ваш подход и тот описанный в ссылке выше.
Я не сравнивал, предоставил лишь ссылки по постам относительно импорта в Runtime что их объединяет.
Конечно хотелось бы разобраться как там всё устроенно, чтобы попытаться решить вопрос с поддержкой всех типов шрифтов, но я не располагаю, пока что таким количеством времени, потому решил вопрос так как описано выше.

p.s. конвертировав otf в ttf, для использования его с помощью этого, не давало результатов, в моем проекте первым что нужно использовать Myriad Pro, которого в формате ttf я лично не нашёл, а сидеть у разбитого корыта я не привык, потому собственно и Вы смотрите данную реализацию. На сколько она оправдана, это еще предстоит выяснить, не исключено что всё же есть другие методы решения данного вопроса с полной поддержкой шрифтов, как при "ручном" импорте.
 
Последние записи от AlexCooper

 


Часовой пояс GMT +4, время: 14:22.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.