青空文庫リーダー(※wonderfl上では動きません)
"Use Flash Player 10 text libraries (FTE).
* You can do a lot of interesting things with text,
* but I think it has even more potential
* in experimental work where
* the APIs are used just as a container format
* for images, movie clips
* and other interactive elements."
* by Justin Everett-Church
*
* This code is an example of FTE.
なんちゃって青空文庫リーダー
* ※wonderfl上から青空文庫のサイトのデータを直接は読めません。
* ソースをダウンロードして、ローカルで動かしてみてください。
*
* 縦書き半端です(─や…が横のまま)
* ルビは力技です(まともな方法を調べても全然見つからなかった…)
*
* 全部一枚のSpriteに貼り付けてるので長い文章だと落ちます
*
* Kenichi UENO(Keno)
/**
* Copyright keno42 ( http://wonderfl.net/user/keno42 )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/3uAc
*/
/**
*
* "Use Flash Player 10 text libraries (FTE).
* You can do a lot of interesting things with text,
* but I think it has even more potential
* in experimental work where
* the APIs are used just as a container format
* for images, movie clips
* and other interactive elements."
* by Justin Everett-Church
*
* This code is an example of FTE.
*/
/**
* なんちゃって青空文庫リーダー
* ※wonderfl上から青空文庫のサイトのデータを直接は読めません。
* ソースをダウンロードして、ローカルで動かしてみてください。
*
* 縦書き半端です(─や…が横のまま)
* ルビは力技です(まともな方法を調べても全然見つからなかった…)
*
* 全部一枚のSpriteに貼り付けてるので長い文章だと落ちます
*
* Kenichi UENO(Keno)
*/
package {
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.SecurityErrorEvent;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.system.System;
import flash.text.engine.*;
import flash.text.TextField;
import flash.ui.Keyboard;
import flash.utils.ByteArray;
[SWF(width = "465", height = "465", backgroundColor = "0xFFFFFF", fps = "30")]
public class AdobeFTE extends Sprite {
private var bookXML:XML;
private var TEXT:String = "Use Flash Player 10 text libraries (FTE). You can do a lot of interesting things with text, but I think it has even more potential in experimental work where the APIs are used just as a container format for images, movie clips and other interactive elements.";
private var textArray:Array;
private var textReadingArray:Array;
private var ns:String;
private var tx:Number = 0;
private var ty:Number = 0;
private var paper:Sprite = new Sprite();
public function AdobeFTE() {
var tf:TextField = new TextField();
tf.type = "input";
tf.text = "http://www.aozora.gr.jp/cards/000879/files/3814_27290.html";
tf.autoSize = "left";
tf.border = true;
tf.addEventListener(KeyboardEvent.KEY_DOWN, onTfKeyDown);
this.addChild( tf );
this.addChild( paper );
load(tf.text);
}
protected function onTfKeyDown(e:KeyboardEvent):void {
if ( e.keyCode == Keyboard.ENTER ) {
load( e.target.text );
}
}
protected function load(url:String):void {
// init
while ( paper.numChildren ) paper.removeChildAt(0);
paper.x = paper.y = 0;
offsetX = 425;
offsetY = 32;
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
urlLoader.addEventListener(Event.COMPLETE, onComplete);
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.load(new URLRequest(url));
}
protected function onSecurityError(e:SecurityErrorEvent):void {
trace(e);
}
protected function onComplete(e:Event):void {
// load as binary and read by shift_jis encoding
var byteData:ByteArray = ByteArray(e.target.data);
bookXML = new XML(byteData.readMultiByte(byteData.length, "shift_jis"));
ns = bookXML.namespaceDeclarations().toString();
default xml namespace = new Namespace(ns);
TEXT = bookXML.body.toString();
TEXT = TEXT.replace(/ /g, "");
TEXT = TEXT.replace(/(\r|\n)/g, "");
TEXT = TEXT.split(/<\/h[12]>/g).join("\n");
TEXT = TEXT.split(/<br\/>/g).join("\n");
textArray = TEXT.split("\n");
for ( var i:int = 0; i < textArray.length; i++ ) {
var array:Array = [];
var temp:String = textArray[i];
var matchArray:Array = temp.match(/<rt>([^<\/]*)<\/rt/g);
var tempArray:Array = temp.split("<ruby><rb>");
for ( var j:int = 0; j < tempArray.length; j++ ) {
if ( j > 0 ) {
array.push( matchArray[j - 1].substr(4, matchArray[j - 1].length - 8) );
}
tempArray[j] = tempArray[j].replace(/<rp>.*<\/ruby>/g, "");
tempArray[j] = tempArray[j].replace(/<[^>]*>/g, "");
array.push( tempArray[j] );
}
textArray[i] = array;
}
setup();
}
private function setup():void {
for ( var i:int = 0; i < textArray.length; i++ ) {
var tempText:String = "";
var tempSp:Sprite = new Sprite();
for ( var j:int = 0; j < textArray[i].length; j++ ) {
if ( j & 1 ) {
// this.graphics.drawCircle(lastX, lastY, j + 1); // debug
var ruby:TextLine = createTextBlock( createContentElement( textArray[i][j], createElementFormat( fontDescription, true ) ), textJustifier ).createTextLine(null, 100);
paper.addChild( ruby );
ruby.x = lastX;
if ( lastY != offsetY && (tempText.substr( -1, 1) == "、" || tempText.substr( -1, 1) == "。") ) {
lastY += 8;
}
ruby.y = lastY;
} else {
if ( textArray[i][j] == "" ) textArray[i][j] = " ";
tempText += textArray[i][j];
// FontDescription : フォント設定
var fontDescription:FontDescription = createFontDescription();
// ElementFormat
var elementFormat:ElementFormat = createElementFormat( fontDescription );
// ContentElement
var contentElement: ContentElement = createContentElement( tempText, elementFormat );
// TextJustifier
var textJustifier:TextJustifier =createTextJustifier();
// TextBlock
var textBlock:TextBlock = createTextBlock( contentElement, textJustifier );
if( j < textArray[i].length-1 ){
layout( textBlock, tempSp );
} else {
layout( textBlock, paper );
}
}
}
}
tempSp = null;
this.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onKeyDown(e:KeyboardEvent):void {
switch( e.keyCode ) {
case Keyboard.LEFT:
tx += 35;
if ( tx > (paper.width - 300 )) tx -= 35;
break;
case Keyboard.RIGHT:
tx -= 35;
if ( tx < -80 ) tx += 35;
break;
case Keyboard.DOWN:
tx += 400;
if ( tx > (paper.width - 300 )) tx = (paper.width - 300 );
break;
case Keyboard.UP:
tx -= 400;
if ( tx < -80 ) tx = -80;
break;
}
}
private function onEnterFrame(e:Event):void {
paper.x += ( tx - paper.x ) * 0.25;
paper.y += ( ty - paper.y ) * 0.25;
}
private var offsetX:int = 425;
private var offsetY:int = 32;
private var lastX:int;
private var lastY:int;
private var lineWidth:Number=2.5;
private function layout(textBlock:TextBlock, output:DisplayObjectContainer):void {
if ( output != paper ) {
var oldOffsetX:int = offsetX;
var oldOffsetY:int = offsetY;
}
var textLine:TextLine = textBlock.createTextLine ( null, 400 );
// this.graphics.lineStyle(0, 0xFF0000);
// this.graphics.drawCircle(offsetX, offsetY, 2); // debug
while (textLine) {
output.addChild(textLine);
textLine.x = offsetX;
textLine.y = offsetY;
this.graphics.drawRect(offsetX, offsetY, textLine.width, textLine.height); // debug
lastX = offsetX + textLine.width;
lastY = offsetY + textLine.height;
if ( lastY >= 425 ) { // やっつけ
lastX -= textLine.textHeight * lineWidth;
lastY = offsetY;
}
offsetX -= textLine.textHeight * lineWidth;
textLine = textBlock.createTextLine( textLine, 400 );
}
if ( output != paper ) {
offsetX = oldOffsetX;
offsetY = oldOffsetY;
}
}
// FontDescription : フォント設定
private function createFontDescription():FontDescription {
var fontDescription :FontDescription = new FontDescription();
// CFFHinting.NONE, // CFFHinting.HORIZONTAL_STEM
fontDescription.cffHinting = CFFHinting.HORIZONTAL_STEM;
// FontLookup.DEVICE, FontLookup.EMBEDDED_CFF
fontDescription.fontLookup = FontLookup.DEVICE;
//_ゴシック, 明朝, _等幅, _sans, _serif, _typewriter
fontDescription.fontName = "明朝, _sans";
// FontPosture.NORMAL, FontPosture.ITALIC
fontDescription.fontPosture = FontPosture.NORMAL;
// FontWeight.NORMAL, FontWeight.BOLD
fontDescription.fontWeight = FontWeight.NORMAL;
// true, false
fontDescription.locked = false;
// RenderingMode.NORMAL, RenderingMode.CFF
fontDescription.renderingMode = RenderingMode.CFF;
return fontDescription;
}
// ElementFormat : テキストフォーマット設定
private function createElementFormat( fontDescription:FontDescription, isRuby:Boolean = false ):ElementFormat {
var elementFormat:ElementFormat = new ElementFormat();
// TextBaseline.ROMAN, TextBaseline.ASCENT, TextBaseline.DESCENT, TextBaseline.IDEOGRAPHIC_TOP, TextBaseline.IDEOGRAPHIC_CENTER, TextBaseline.IDEOGRAPHIC_BOTTOM
elementFormat.alignmentBaseline = TextBaseline.ROMAN;
// Number
elementFormat.alpha = 1;
// Number
elementFormat.baselineShift = 0;
// BreakOpportunity.AUTO, BreakOpportunity.ANY, BreakOpportunity.NONE, BreakOpportunity.ALL
elementFormat.breakOpportunity = BreakOpportunity.AUTO;
// uint
elementFormat.color = 0x000000;
// DigitCase.DEFAULT, DigitCase.LINING, DigitCase.OLD_STYLE
elementFormat.digitCase = DigitCase.DEFAULT;
// DigitWidth.DEFAULT, DigitWidth.PROPORTIONAL, DigitWidth.TABULAR
elementFormat.digitWidth = DigitWidth.TABULAR;
// TextBaseline.ROMAN, TextBaseline.ASCENT, TextBaseline.DESCENT, TextBaseline.IDEOGRAPHIC_TOP, TextBaseline.IDEOGRAPHIC_CENTER, TextBaseline.IDEOGRAPHIC_BOTTOM
elementFormat.dominantBaseline = TextBaseline.ROMAN;
// FontDescription
elementFormat.fontDescription = fontDescription;
// Number
elementFormat.fontSize = isRuby?8: 15;
// Kerning.ON, Kerning.OFF, Kerning.AUTO
elementFormat.kerning= Kerning.ON;
// LigatureLevel.NONE, LigatureLevel.MINIMUM, LigatureLevel.COMMON, LigatureLevel.UNCOMMON, LigatureLevel.EXOTIC
elementFormat.ligatureLevel = LigatureLevel.NONE;
// String
elementFormat.locale = "ja";
// true, false
elementFormat.locked= false;
// TextRotation.ROTATE_0, TextRotation.ROTATE_90, TextRotation.ROTATE_180, TextRotation.ROTATE_270, TextRotation.AUTO
elementFormat.textRotation = TextRotation.ROTATE_270;
// Number
elementFormat.trackingLeft = 0;
// Number
elementFormat.trackingRight = 0;
// TypographicCase.DEFAULT, TypographicCase.TITLE, TypographicCase.CAPS, TypographicCase.SMALL_CAPS, TypographicCase.UPPERCASE, TypographicCase.LOWERCASE, TypographicCase.CAPS_AND_SMALL_CAPS
elementFormat.typographicCase= TypographicCase.DEFAULT;
return elementFormat;
}
// ContentElement : 表示内容
private function createContentElement( content:*, elementFormat:ElementFormat ):ContentElement {
// GraphicElement, GroupElement, TextElement
var contentElement: ContentElement = new TextElement();
//var graphicElement: GraphicElement= new GraphicElement();
//var groupElement: GroupElement= new GroupElement();
// ElementFormat
contentElement.elementFormat = elementFormat;
// EventDispatcher,
contentElement.eventMirror = null;
// GroupElement
contentElement.groupElement;// read-only
// String
contentElement.rawText;// read-only
// String
contentElement.text; // read-only
// TextBlock
contentElement.textBlock;// read-only
// int
contentElement.textBlockBeginIndex;// read-only
// TextRotation.ROTATE_0, TextRotation.ROTATE_90, TextRotation.ROTATE_180, TextRotation.ROTATE_270, TextRotation.AUTO
contentElement.textRotation = TextRotation.ROTATE_0;
// *
contentElement.userData;
// for SubClass's elements
switch( true ) {
case contentElement is TextElement:
var textElement: TextElement = contentElement as TextElement;
var str:String = content as String;
textElement.text = str;
break;
case contentElement is GraphicElement:
var graphicElement: GraphicElement= contentElement as GraphicElement;
var display:DisplayObject = content as DisplayObject;
graphicElement.elementWidth = display.width;
graphicElement.elementHeight = display.height;
graphicElement.graphic= display;
break;
case contentElement is GroupElement:
var groupElement: GroupElement= contentElement as GroupElement;
groupElement.elementCount; // read-only
// getElementAt( index:int ):ContentElement;
// getElementAtCharIndex( charIndex:int ):ContentElement;
// mergeTextElements( element:ContentElement ):int;
// groupElements( beginIndex:int, endIndex:int ):GroupElement;
// replaceElements( beginIndex:int, endIndex:int ):TextElement;
// setElements( value:Vector.<ContentElement> ):void;
// splitTextElement( elementIndex:int, splitIndex:int ):TextElement;
// ungroupElements( groupIndex:int ):void;
break;
}
return textElement;
}
// TextJustifier : 整列設定
private function createTextJustifier():TextJustifier {
// EastAsianJustifier, SpaceJustifier,
var textJustifier:TextJustifier = new EastAsianJustifier("ja");
// var textJustifier:TextJustifier = new SpaceJustifier("ja");
// LineJustification.UNJUSTIFIED, LineJustification.ALL_BUT_LAST, LineJustification.ALL_INCLUDING_LAST
textJustifier.lineJustification = LineJustification.UNJUSTIFIED;
switch( true ) {
case textJustifier is EastAsianJustifier:
var eastAsianJustifier:EastAsianJustifier = textJustifier as EastAsianJustifier;
// JustificationStyle.PUSH_IN_KINSOKU, JustificationStyle.PUSH_OUT_ONLY, JustificationStyle.PRIORITIZE_LEAST_ADJUSTMENT
eastAsianJustifier.justificationStyle = JustificationStyle.PUSH_IN_KINSOKU;
break;
case textJustifier is SpaceJustifier:
var spaceJustifier:SpaceJustifier= textJustifier as SpaceJustifier;
// true , false
spaceJustifier.letterSpacing = true;
break;
}
return textJustifier;
}
// TextBlock : 段組設定
private function createTextBlock( content:ContentElement, textJustifier:TextJustifier ):TextBlock {
var textBlock:TextBlock = new TextBlock();
// true, false
textBlock.applyNonLinearFontScaling = true;
// FontDescription
textBlock.baselineFontDescription;
// Number
textBlock.baselineFontSize = 24;
// TextBaseline.ROMAN, TextBaseline.ASCENT, TextBaseline.DESCENT, TextBaseline.IDEOGRAPHIC_TOP, TextBaseline.IDEOGRAPHIC_CENTER, TextBaseline.IDEOGRAPHIC_BOTTOM
textBlock.baselineZero = TextBaseline.ROMAN;
// int
textBlock.bidiLevel
// TextElement, GraphicElement, GroupElement
textBlock.content = content;
// TextLine
textBlock.firstInvalidLine; // read-only
// TextLine
textBlock.firstLine; // read-only
// TextLine
textBlock.lastLine; // read-only
// TextRotation.ROTATE_0, TextRotation.ROTATE_90, TextRotation.ROTATE_180, TextRotation.ROTATE_270, TextRotation.AUTO
textBlock.lineRotation = TextRotation.ROTATE_90;
// Vector.<TabStop>
textBlock.tabStops;
// EastAsianJustifier, SpaceJustifier,
textBlock.textJustifier = textJustifier;
// TextLineCreationResult.SUCCESS, TextLineCreationResult.COMPLETE, TextLineCreationResult.INSUFFICIENT_WIDTH
textBlock.textLineCreationResult// read-only
// *
textBlock.userData;
return textBlock;
}
}
}