PageRenderTime 58ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/svgweb/tests/non-licensed/perf/test7/src/org/svgweb/nodes/SVGTextNode.as

https://github.com/dionjwa/Hydrax
ActionScript | 413 lines | 290 code | 48 blank | 75 comment | 83 complexity | d9a7294ede974db38c0e9d86c921b74a MD5 | raw file
  1. /*
  2. Copyright (c) 2009 by contributors:
  3. * James Hight (http://labs.zavoo.com/)
  4. * Richard R. Masters
  5. * Google Inc. (Brad Neuberg -- http://codinginparadise.org)
  6. Licensed under the Apache License, Version 2.0 (the "License");
  7. you may not use this file except in compliance with the License.
  8. You may obtain a copy of the License at
  9. http://www.apache.org/licenses/LICENSE-2.0
  10. Unless required by applicable law or agreed to in writing, software
  11. distributed under the License is distributed on an "AS IS" BASIS,
  12. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. See the License for the specific language governing permissions and
  14. limitations under the License.
  15. */
  16. package org.svgweb.nodes
  17. {
  18. import org.svgweb.SVGViewerWeb;
  19. import org.svgweb.core.SVGNode;
  20. import org.svgweb.utils.SVGColors;
  21. import org.svgweb.utils.SVGUnits;
  22. import flash.display.Sprite;
  23. import flash.events.Event;
  24. import flash.text.TextField;
  25. import flash.text.TextFieldAutoSize;
  26. import flash.text.TextFormat;
  27. import flash.text.TextLineMetrics;
  28. import flash.filters.GlowFilter;
  29. /** SVG Text element node **/
  30. public class SVGTextNode extends SVGNode
  31. {
  32. /**
  33. * Hold text path node if text follows a path
  34. **/
  35. private var _textPath:SVGNode = null;
  36. /**
  37. * TextField to render nodes text
  38. **/
  39. private var _textField:TextField;
  40. private var _svgFont:SVGFontNode;
  41. private var _text:String = '';
  42. protected var newGlyphs:Array = null;
  43. protected var newViewBoxSprite:Sprite = new Sprite();
  44. protected var lastGlyph:SVGNode;
  45. public function SVGTextNode(svgRoot:SVGSVGNode, xml:XML, original:SVGNode = null):void {
  46. super(svgRoot, xml, original);
  47. }
  48. protected override function onAddedToStage(event:Event):void {
  49. super.onAddedToStage(event);
  50. svgRoot.registerFontListener(this);
  51. }
  52. protected override function onRemovedFromStage(event:Event):void {
  53. svgRoot.unregisterFontListener(this);
  54. super.onRemovedFromStage(event);
  55. }
  56. override public function onRegisterFont(fontFamily:String):void {
  57. if (fontFamily == this.getStyleOrAttr('font-family')) {
  58. invalidateDisplay();
  59. }
  60. }
  61. /**
  62. * Need to recreate child glyphs when redrawing text
  63. **/
  64. override public function invalidateDisplay():void {
  65. super.invalidateDisplay();
  66. // Need to recreate child glyphs when redrawing text
  67. this._parsedChildren = false;
  68. }
  69. override public function hasText():Boolean {
  70. return true;
  71. }
  72. override public function setText(newValue:String):void {
  73. //this.dbg('setText, newValue='+newValue);
  74. this._xml.setChildren(newValue);
  75. this._text = newValue;
  76. }
  77. /**
  78. * Get any child text (not text inside child nodes)
  79. * If this node has any text create a TextField at this._textField
  80. * Call SVGNode.parse()
  81. **/
  82. override protected function parseChildren():void {
  83. var i:uint;
  84. var textWidth:Number;
  85. var glyphChar:String;
  86. super.parseChildren();
  87. //Check for SVGFont
  88. var fontFamily:String = this.getStyleOrAttr('font-family');
  89. this._svgFont = this.svgRoot.getFont(fontFamily);
  90. var glyph:SVGGlyphNode;
  91. if (this._svgFont != null) {
  92. if (this._textField) {
  93. this._textField.parent.removeChild(this._textField);
  94. this._textField = null;
  95. }
  96. var fontSize:String = this.getStyleOrAttr('font-size');
  97. var fontSizeNum:Number = SVGUnits.cleanNumber(fontSize);
  98. var glyphXOffsets:Array = new Array();
  99. var xString:String = super.getAttribute('x', '0');
  100. xString = xString.replace(/,/sg," "); //Replace commas with spaces
  101. glyphXOffsets = xString.split(/\s+/);
  102. var glyphYOffsets:Array = new Array();
  103. var yString:String = super.getAttribute('y', '0');
  104. yString = yString.replace(/,/sg," "); //Replace commas with spaces
  105. glyphYOffsets = yString.split(/\s+/);
  106. var startGlyphX:Number = 0;
  107. var startGlyphY:Number = 0;
  108. // Handle text-anchor attribute
  109. var textAnchor:String = this.getStyleOrAttr('text-anchor');
  110. var currentNode:SVGNode = this;
  111. while (textAnchor == 'inherit') {
  112. if (currentNode.svgParent != null) {
  113. currentNode = currentNode.svgParent;
  114. textAnchor = currentNode.getStyleOrAttr('text-anchor');
  115. }
  116. else {
  117. textAnchor = null;
  118. }
  119. }
  120. switch (textAnchor) {
  121. case 'middle':
  122. textWidth=0;
  123. for (i = 0; i < this._text.length; i++) {
  124. glyphChar = this._text.charAt(i);
  125. glyph = this._svgFont.getGlyph(glyphChar);
  126. textWidth += SVGUnits.cleanNumber(glyph.getAttribute('horiz-adv-x'));
  127. }
  128. startGlyphX = -textWidth / 2;
  129. break;
  130. case 'end':
  131. textWidth=0;
  132. for (i = 0; i < this._text.length; i++) {
  133. glyphChar = this._text.charAt(i);
  134. glyph = this._svgFont.getGlyph(glyphChar);
  135. textWidth += SVGUnits.cleanNumber(glyph.getAttribute('horiz-adv-x'));
  136. }
  137. startGlyphX = -textWidth;
  138. break;
  139. default: //'start'
  140. startGlyphX=0;
  141. break;
  142. }
  143. var glyphX:Number = startGlyphX;
  144. var glyphY:Number = startGlyphY;
  145. newGlyphs = new Array();
  146. //Add a glyph for each character in the text
  147. for (i = 0; i < this._text.length; i++) {
  148. glyphChar = this._text.charAt(i);
  149. glyph = this._svgFont.getGlyph(glyphChar);
  150. var glyphClone:SVGNode = glyph.clone();
  151. if (this.getStyleOrAttr('fill')) {
  152. glyphClone.setAttribute('fill', this.getStyleOrAttr('fill'));
  153. }
  154. glyphClone.setAttribute('transform',
  155. 'scale(' + (fontSizeNum / 2048) + ') scale(1,-1)');
  156. // If there is an offset for each character, then apply the offset,
  157. // adjusting for the transform above.
  158. if (glyphXOffsets.length >= 2 && glyphXOffsets.length > i) {
  159. glyphX = (startGlyphX + glyphXOffsets[i]) * (2048/fontSizeNum);
  160. }
  161. if (glyphYOffsets.length >= 2 && glyphYOffsets.length > i) {
  162. glyphY = (startGlyphY + glyphYOffsets[i]) * (-2048/fontSizeNum);
  163. }
  164. glyphClone.setAttribute('x', String(glyphX));
  165. glyphClone.setAttribute('y', String(glyphY));
  166. // If there is not an offset for each character, then use
  167. // the standard horizontal adjustment.
  168. if (glyphXOffsets.length < 2 || glyphXOffsets.length <= i) {
  169. glyphX = glyphX + SVGUnits.cleanNumber(glyph.getAttribute('horiz-adv-x'));
  170. }
  171. glyphClone.topSprite.visible=false;
  172. newGlyphs.push(glyphClone);
  173. addSVGChild(glyphClone);
  174. lastGlyph=glyphClone;
  175. }
  176. }
  177. else {
  178. if (this._textField == null) {
  179. //If this is not an SVGFont, use a TextField
  180. this._textField = new TextField();
  181. }
  182. }
  183. }
  184. public function onDrawGlyph(glyph:SVGNode):void {
  185. // when they are ready, unhide the new characters,
  186. // and remove the old
  187. if (glyph == lastGlyph && newGlyphs != null) {
  188. for (var i:uint=0; i < this.svgChildren.length; i++) {
  189. if (this.svgChildren[i] is SVGGlyphNode
  190. && newGlyphs.indexOf(this.svgChildren[i]) == -1) {
  191. removeSVGChild(this.svgChildren[i]);
  192. i--;
  193. }
  194. else {
  195. this.svgChildren[i].topSprite.visible=true;
  196. }
  197. }
  198. newGlyphs = null;
  199. }
  200. }
  201. override public function getAttribute(name:String, defaultValue:* = null,
  202. inherit:Boolean = true,
  203. applyAnimations:Boolean = true,
  204. useStyle:Boolean = false):* {
  205. if (name == 'stroke-width' && this._textField == null) {
  206. // Relevant to SVG Fonts only
  207. var fontSize:String = this.getStyleOrAttr('font-size');
  208. var fontSizeNum:Number = SVGUnits.cleanNumber(fontSize);
  209. var strokeWidthStr:String = super.getAttribute(name, defaultValue, inherit, true, true);
  210. var strokeWidth:Number = SVGUnits.cleanNumber(strokeWidthStr);
  211. strokeWidth = strokeWidth * (1024 / fontSizeNum);
  212. return String(strokeWidth);
  213. } else if ( (name == "x" || name == "y") ) {
  214. var xString:String = super.getAttribute(name, defaultValue, inherit, applyAnimations, false);
  215. if (xString != null) {
  216. xString = xString.replace(/,/sg," "); //Replace commas with spaces
  217. if (xString.split(/\s+/).length >= 2) {
  218. // For SVG Fonts, if there is more than one value, then apply them to
  219. // individual glyphs, not the text node
  220. if (this._textField == null) {
  221. return "0";
  222. }
  223. else {
  224. // Issue 405: We do not currently support glyph placement
  225. // for native fonts. Just use the first value.
  226. return xString.split(/\s+/)[0];
  227. }
  228. }
  229. else {
  230. return xString;
  231. }
  232. }
  233. else {
  234. return xString;
  235. }
  236. }
  237. else {
  238. return super.getAttribute(name, defaultValue, inherit, applyAnimations, useStyle);
  239. }
  240. }
  241. /**
  242. * Call SVGNode.setAttributes()
  243. * If this node contains text load text format (font, font-size, color, etc...)
  244. * Render text to a bitmap and add bitmap to node
  245. **/
  246. override protected function setAttributes():void {
  247. super.setAttributes();
  248. if (this._textField != null) {
  249. var fontFamily:String = this.getStyleOrAttr('font-family');
  250. var fontSize:String = this.getStyleOrAttr('font-size');
  251. var fill:String = this.getStyleOrAttr('fill');
  252. if (fill == 'currentColor') {
  253. fill = this.getStyleOrAttr('color');
  254. }
  255. var fontWeight:String = this.getStyleOrAttr('font-weight');
  256. var textAnchor:String = this.getStyleOrAttr('text-anchor');
  257. var textFormat:TextFormat = this._textField.getTextFormat();
  258. if (fontFamily != null) {
  259. fontFamily = fontFamily.replace("'", '');
  260. textFormat.font = fontFamily;
  261. }
  262. if (fontSize != null) {
  263. //Handle floating point font size
  264. var fontSizeNum:Number = SVGUnits.cleanNumber(fontSize);
  265. //Font size can be in user units, pixels (px), or points (pt); if no
  266. //measurement type given defaults to user units
  267. if (SVGUnits.getType(fontSize) == SVGUnits.PT) {
  268. fontSizeNum = SVGUnits.pointsToPixels(fontSizeNum);
  269. }
  270. var fontScale:Number = Math.floor(fontSizeNum);
  271. textFormat.size = fontScale;
  272. fontScale = fontSizeNum / fontScale;
  273. _textField.scaleX = fontScale;
  274. _textField.scaleY = fontScale;
  275. }
  276. if (fill != null) {
  277. textFormat.color = SVGColors.getColor(fill);
  278. }
  279. // only bold/no bold supported for now (SVG has many levels of bold)
  280. var currentNode:SVGNode = this;
  281. while (fontWeight == 'inherit') {
  282. if (currentNode.svgParent != null) {
  283. currentNode = currentNode.svgParent;
  284. fontWeight = currentNode.getStyleOrAttr('font-weight');
  285. }
  286. else {
  287. fontWeight = null;
  288. }
  289. }
  290. if (fontWeight != null && fontWeight != 'normal') {
  291. textFormat.bold = true;
  292. }
  293. this._textField.text = this._xml.text().toString();
  294. this._textField.setTextFormat(textFormat);
  295. var textLineMetrics:TextLineMetrics = this._textField.getLineMetrics(0);
  296. currentNode = this;
  297. while (textAnchor == 'inherit') {
  298. if (currentNode.svgParent != null) {
  299. currentNode = currentNode.svgParent;
  300. textAnchor = currentNode.getStyleOrAttr('text-anchor');
  301. }
  302. else {
  303. textAnchor = null;
  304. }
  305. }
  306. // Handle text-anchor attribute
  307. switch (textAnchor) {
  308. case 'middle':
  309. this._textField.autoSize = TextFieldAutoSize.CENTER;
  310. this._textField.x = Math.floor( - (this._textField.width - textLineMetrics.width)/2
  311. - textLineMetrics.width / 2 );
  312. break;
  313. case 'end':
  314. this._textField.autoSize = TextFieldAutoSize.RIGHT;
  315. // Technically, the entire difference between text field width and
  316. // the actual text width should be shifted in the following equation,
  317. // assuming the text was actually right justified properly,
  318. // but half seems to replicate native behavior more closely.
  319. this._textField.x = - Math.floor( (this._textField.width - textLineMetrics.width)/2)
  320. - textLineMetrics.width;
  321. break;
  322. default: //'start'
  323. this._textField.autoSize = TextFieldAutoSize.LEFT;
  324. this._textField.x = - Math.floor(this._textField.width - textLineMetrics.width)/2;
  325. //If autosize actually left justified properly, it seems the following would be correct:
  326. //this._textField.x = 0;
  327. break;
  328. }
  329. this.setVisibility(this.getStyleOrAttr('visibility'));
  330. // SVG Text elements position y attribute as baseline of text,
  331. // not the top
  332. this._textField.y = 0 - textLineMetrics.ascent - 1;
  333. }
  334. }
  335. override protected function setVisibility(visible:String,
  336. recursive:Boolean = false):void {
  337. // Surprisingly, this.alpha does not work as expected on
  338. // text system fonts; a work around is needed. See
  339. // http://oddhammer.com/tutorials/alpha_dynamic_text/
  340. // for details. Basically you have to embed the text into
  341. // a filter which turns it into a bitmap, and then apply the
  342. // alpha!
  343. if (visible == 'hidden') {
  344. var filter:GlowFilter = new GlowFilter(0x000000, .1, 16, 16,
  345. 0, 3, false, false);
  346. this.drawSprite.filters = new Array(filter);
  347. this.drawSprite.alpha = 0;
  348. }
  349. else {
  350. this.drawSprite.filters = new Array();
  351. super.setVisibility(visible);
  352. }
  353. }
  354. override protected function draw():void {
  355. super.draw();
  356. if (this._textField != null) {
  357. drawSprite.addChild(this._textField);
  358. }
  359. }
  360. }
  361. }