/* Copyright (c) 2009 Yahoo! Inc. All rights reserved. The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license */ package com.yahoo.astra.display { import flash.display.Sprite; import flash.text.*; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.PixelSnapping; import flash.geom.Matrix; /** * BitmapText is a class that can be used to create rotating text fields with * device fonts. When the rotation property is set to a value other than 0 * and the embedFonts is false, the text field will be replaced * by a Sprite containing a bitmap. * * @author Tripp Bridges */ public class BitmapText extends Sprite { /** * Constructor */ public function BitmapText() { super(); this.addChild(_bitmapContainer); this.addChild(this.textField); } /** * @private * Used to hold the bitmap object */ private var _bitmap:Bitmap = null; /** * @private * Sprite that holds the bitmap container */ private var _bitmapContainer:Sprite = new Sprite(); /** * @private * Placeholder for the width value. Equals the width of a bitmap when the bitmap * displays text and the text field when it displays text. Since the text field and * bitmap sprite are always present, the returned dimensions would always be innacurate. */ private var _width:Number = 0; /** * @private * Placeholder for the height value. Equals the height of a bitmap when the bitmap * displays text and the text field when it displays text. Since the text field and * bitmap sprite are always present, the returned dimensions would always be innacurate. */ private var _height:Number = 0; /** * @private * Placeholder for the text field */ private var _textField:TextField = new TextField(); /** * Specifies the format applied to newly inserted text, such as text inserted with the * replaceSelectedText() method or text entered by a user. */ public function get defaultTextFormat():TextFormat { return this.textField.defaultTextFormat; } /** * @private (setter) */ public function set defaultTextFormat(value:TextFormat):void { this.textField.defaultTextFormat = value; } /** * Specifies whether to render by using embedded font outlines. */ public function get embedFonts():Boolean { return this.textField.embedFonts; } /** * @private (setter) */ public function set embedFonts(value:Boolean):void { this.textField.embedFonts = value; } /** * @private * Placeholder for the rotation property of the text field */ private var _rotation:Number = 0; /** * @inheritDoc */ override public function get rotation():Number { return _rotation; } /** * @private (setter) */ override public function set rotation(value:Number):void { _rotation = value; setTextRotation(value); } /** * A string that is the current text in the text field. */ public function get text():String { return this.textField.text; } /** * @private (setter) */ public function set text(value:String):void { this.textField.text = value; this.textField.visible = true; this.contentWidth = _width = this.textField.width; this.contentHeight = _height = this.textField.height; } /** * A Boolean value that indicates whether the text field is selectable. */ public function get selectable():Boolean { return this.textField.selectable; } /** * @private (setter) */ public function set selectable(value:Boolean):void { this.textField.selectable = value; } /** * Controls automatic sizing and alignment of text fields. */ public function get autoSize():String { return this.textField.autoSize; } /** * @private (setter) */ public function set autoSize(value:String):void { this.textField.autoSize = value; } /** * Reference to the text field */ public function get textField():TextField { return _textField; } /** * @private (setter) */ public function set textField(value:TextField):void { _textField = value; } /** * @private (setter) */ public function set border(value:Boolean):void { this.textField.border = value; } /** * Gets the minimum distance for placement of labels of the same rotation */ public function get rotationWidth():Number { var adjustedWidth:Number = 0; if(this.rotation == 0 || Math.abs(this.rotation) == 90) { adjustedWidth = this.width; } else if(this.rotation > 0) { if(this.rotation < 15) { adjustedWidth = this.width - (Math.sin(Math.abs(this.rotation)*Math.PI/180)*this.textField.textHeight*2); } else if(this.rotation > 40 && this.rotation < 50) { adjustedWidth = this.contentHeight / (Math.cos((90-Math.abs(this.rotation))*Math.PI/180)); adjustedWidth = Math.min(adjustedWidth, this.width); } else { adjustedWidth = this.contentHeight / (Math.cos((90-Math.abs(this.rotation))*Math.PI/180)); adjustedWidth = Math.min(adjustedWidth, this.width); } } else { if(this.rotation > -15) { adjustedWidth = this.width - (Math.sin(Math.abs(this.rotation)*Math.PI/180)*this.textField.textHeight*2); } else if(this.rotation < -40 && this.rotation > -50) { adjustedWidth = this.contentHeight / (Math.cos((90-Math.abs(this.rotation))*Math.PI/180)); adjustedWidth = Math.min(adjustedWidth, this.width); } else { adjustedWidth = this.contentHeight / (Math.cos((90-Math.abs(this.rotation))*Math.PI/180)); adjustedWidth = Math.min(adjustedWidth, this.width); } } return adjustedWidth; } public function get rotationHeight():Number { var adjustedHeight:Number; if(this.rotation == 0 || Math.abs(this.rotation) == 90) { adjustedHeight = this.height; } else { if(Math.abs(this.rotation) > 70) { adjustedHeight = this.height; } else { adjustedHeight = this.contentHeight / (Math.sin((90 - Math.abs(this.rotation))*Math.PI/180)); } } return adjustedHeight; } /** * @private * Placeholder for contentWidth */ private var _contentWidth:Number = 0; /** * Width of the text field without rotation. */ public function get contentWidth():Number { return _contentWidth; } /** * @private (setter) */ public function set contentWidth(value:Number):void { _contentWidth = value; } /** * @private * Placeholder for contentHeight */ private var _contentHeight:Number = 0; /** * Height of the text field without rotation. */ public function get contentHeight():Number { return _contentHeight; } /** * @private (setter) */ public function set contentHeight(value:Number):void { _contentHeight = value; } public function get textWidth():Number { return this.textField.textWidth; } public function get textHeight():Number { return this.textField.textHeight; } /** * @private * * Sets the rotation of the text. When the value is 0 or embedFonts * is set to true, rotate the text field. Otherwise, draw the text field into a bitmap * and rotate its container sprite. */ private function setTextRotation(value:Number):void { if(this.text == null || this.text == "") return; if(value == 0 || this.embedFonts) { this.contentWidth = this.textField.width; this.contentHeight = this.textField.height; this.textField.visible = true; this.textField.x = 0; this.textField.y = 0; _bitmapContainer.visible = false; } else { var smoothing:Boolean = Math.abs(value)%90 != 0; var matrix:Matrix = new Matrix(); //Have to move text over if it is right-aligned. Need to add an extra 5 pixels to the x value. We will also add 5 pixels to //the width argument of the bitmap data. Depending on the letters, the left-most letters get cut-off. The additional 5 pixels //prevents this from happening. if(this.autoSize == TextFieldAutoSize.RIGHT) matrix.translate(-Math.ceil(this.textField.x)+5, -Math.ceil(this.textField.y)); var wid:Number = this.autoSize == TextFieldAutoSize.RIGHT ? (this.textField.width) + 5 : this.textField.width; var bitmapDataText:BitmapData = new BitmapData(Math.ceil(wid), Math.ceil(this.textField.height), true, 0x000000); bitmapDataText.draw(textField, matrix, null, null, null, smoothing); _bitmap = new Bitmap(bitmapDataText); _bitmap.smoothing = smoothing; if(_bitmapContainer.numChildren > 0) _bitmapContainer.removeChildAt(0); this.textField.visible = false; _bitmapContainer.addChild(_bitmap); this.contentWidth = _bitmapContainer.width; this.contentHeight = _bitmapContainer.height; _bitmapContainer.visible = true; } super.rotation = value; } } }