AS3 custom TextField text is being drawn outside its textWidth - actionscript-3

So this one is a little hard to explain. I have a custom Text class that automatically resizes and sets the width of the text when you change its value. I then take that Text and draw it on a Bitmap to scale it up to make the text look pixelated.
I have a property called maxWidth that allows you to restrict the width of the text if you want it to maintain a certain width. By default the maxWidth is the width of the text's parent so that it doesn't get cut off or expand the parent's boundaries unexpectedly.
So unfortunately when I draw the text it sometimes gets cut off on the right side. Now I've checked all the values and the width and textWidth are showing up as within their maxWidth values, but when I take a look myself through screenshots I see the text is actually about 3 pixels wider than it should be.
Here's an image to better explain what I mean:
I turned on borders so you can easily see what I mean. The word "and" on the first line gets drawn outside its border. Here is the line of code that handles resizing text when you change its bounds.
override protected function checkResize(value:String):void {
var bufferWidth:uint = Math.floor(Number(defaultTextFormat.size) / bufferDivisor) + bufferMin;
var maxWidth:Number = this.maxWidth;
x = y = 0;
if (parent is Stage) {
var stageParent:Stage = Stage(parent);
super.width = stageParent.stageWidth;
super.height = stageParent.stageHeight;
if (maxWidth == 0) maxWidth = stageParent.stageWidth;
}
else {
super.width = parent.width;
super.height = parent.height;
if (maxWidth == 0) maxWidth = parent.width;
}
maxWidth = maxWidth / scale;
text = value;
if (textWidth + bufferWidth <= maxWidth) super.width = textWidth + bufferWidth;
else super.width = maxWidth;
super.height = textHeight + 4;
if (textSnapshot) updateSnapshot();
if (alignRelation) Align.alignTo(textSprite, alignRelation, alignDirection, alignXOffset, alignYOffest);
}
And for this text specifically the width value states it's 512, which is correct since that's the maxWidth. However if you notice the top line in the text, it goes beyond the 512 width border, it actually goes all the way to 515 even though it says its width is 512. Even more bizarre is the textWidth states it's 510.4 even though the first line goes well beyond that amount. I just want to know if I'm doing anything wrong or if there's a way to get a true textWidth value.

This seems to be related to embedding fonts, at least it was when I had the same problem. A workaround is to set the right margin of the text field, like so
var tf:TextFormat = new TextFormat();
tf.rightMargin = 10; // or whatever fixes your problem, e.g. relate it to font size
textField.setTextFormat(tf);

Related

scale font lower than 0.01 is handled as 0.01

Every value I set for .scale() lower than 0.01 will be handled as I set 0.01, rendering a text that is not lower than when I set it to 0.01.
FreetypeFontLoader.FreeTypeFontLoaderParameter sizeParams = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
sizeParams.fontFileName = "MyFont.ttf";
sizeParams.fontParameters.size = (int)Math.ceil(2*MINIMUM_VIEWPORT_SIZE_PIXEL/9f/2f/2f);
sizeParams.fontParameters.color = new Color(Color.Red);
sizeParams.fontParameters.borderColor = new Color(Color.Green);
sizeParams.fontParameters.borderWidth = 2;
sizeParams.fontParameters.minFilter = Texture.TextureFilter.Linear;
sizeParams.fontParameters.magFilter = Texture.TextureFilter.Linear;
assetManager.load("MyFont.ttf", BitmapFont.class, sizeParams);
assetManager.finishLoading();
BitmapFont fontFreeType = assetManager.get("myFont.ttf", BitmapFont.class);
Label.LabelStyle miniLabelStyle = new Label.LabelStyle();
miniLabelStyle.font = fontFreeType;
miniLabelStyle.font.getData().scale(0.005f);
Label labelDebug = new Label("my sample text", game.miniLabelStyle);
I tried this, without any change (either setting true or false):
miniLabelStyle.font.setUseIntegerPositions(false);
I tried this, but the text results so grainy:
labelDebug.setFontScale(0.5f);
How to get a lower scale than 0.01?
This is just a guess, since I don't use FreetypeFontGenerator, but maybe what you're missing is mip mapping, which is necessary for drawing images smaller than their original size without them getting blurry and chunky.
You'll want to make these two settings to enable it, I think:
sizeParams.fontParameters.minFilter = Texture.TextureFilter.MipMapLinearLinear;
sizeParams.fontParameters.genMipMaps = true;
You might also need to add padding around the glyphs so characters don't absorb some of their neighbors when shrunk down. (Settings padTop, padLeft, padBottom, padRight.)
However, there's a reason mip mapping defaults to being disabled. Text looks clearest if it's generated at exactly the size it will be on screen and is rendered with nearest filtering. This isn't universally true, since you might be projecting your text in 3D space or scaling it up and down in real-time. But if it's static text for a GUI, it's probably best to calculate exactly what size it would need to be for it to be one-texture-pixel to one-screen-pixel.

how to make dynamic text move to vertical center if only 1 line is left in AS3

Is there a way to move dynamical text multiline to the center if there is only 1 line left, because veritcal center cannot be adjusted in the properties panel.
Image 3 Lines text and 1 line text
There's no default option for that, but you can emulate it.
// Let's assume this is your TextField you need to v-align.
var TF:TextField;
// Lets also assume that TextField is still in its original place.
// Record its upper Y-coordinate and height.
var atop:Number = TF.y;
var aheight:Number = TF.height;
// These lines instruct the TextField to adjust its lower
// border so that it exactly fits the text inside.
TF.wordWrap = true;
TF.autoSize = TextFieldAutoSize.LEFT;
// Now, math time.
// The difference between original height and new (autosized) height.
var adiff:Number = aheight - TF.height;
// To vertically position the resized TextField into the center of its original
// place, you need to shift it down by half of the height difference.
// It actually works if new height is larger than original too:
// the "adiff" value will be negative and it still works out.
TF.y = atop + adiff / 2;

Adding Letter Spacing in HTML Canvas

I've read a lot of StackOverflow answers and other pages talking about how to do letter spacing in Canvas. One of the more useful ones was Letter spacing in canvas element
As that other question said, 'I've got this canvas element that I'm drawing text to. I want to set the letter spacing similar to the CSS letter-spacing attribute. By that I mean increasing the amount of pixels between letters when a string is drawn.' Note that letter spacing is sometimes, and incorrectly, referred to as kerning.
I notice that the general approach seems to be to output the string on a letter by letter basis, using measureText(letter) to get the letter's width and then adding additional spacing. The problem with this is it doesn't take into account letter kerning pairs and the like. See the above link for an example of this and related comments.
Seems to me that the way to do it, for a line spacing of 'spacing', would be to do something like:
Start at position (X, Y).
Measure wAll, the width of the entire string using measureText()
Remove the first character from the string
Print the first character at position (X, Y) using fillText()
Measure wShorter, the width of the resulting shorter string using measureText().
Subtract the width of the shorter string from the width of the entire string, giving the kerned width of the character, wChar = wAll - wShorter
Increment X by wChar + spacing
wAll = wShorter
Repeat from step 3
Would this not take into account kerning? Am I missing something? Does measureText() add a load of padding that varies depending on the outermost character, or something, and if it does, would not fillText() use the same system to output the character, negating that issue? Someone in the link above mentioned 'pixel-aligned font hinting' but I don't see how that applies here. Can anyone advise either generally or specifically if this will work or if there are problems with it?
EDIT: This is not a duplicate of the other question - which it links to and refers to. The question is NOT about how to do 'letter spacing in canvas', per the proposed duplicate; this is proposing a possible solution (which as far as I know was not suggested by anyone else) to that and other questions, and asking if anyone can see or knows of any issues with that proposed solution - i.e. it's asking about the proposed solution and its points, including details of measureText(), fillText() and 'pixel-aligned font hinting'.
Well, I've written the code, based on the pseudocode above, and done a few comparisons by screenshotting and eyeballing it for differences (zoomed, using straight lines from eg clip boxes to compare X position and width for each character). Looks exactly the same for me, with spacing set at 0.
Here's the HTML:
<canvas id="Test1" width="800px" height="200px"><p>Your browser does not support canvas.</p></canvas>
Here's the code:
this.fillTextWithSpacing = function(context, text, x, y, spacing)
{
//Start at position (X, Y).
//Measure wAll, the width of the entire string using measureText()
wAll = context.measureText(text).width;
do
{
//Remove the first character from the string
char = text.substr(0, 1);
text = text.substr(1);
//Print the first character at position (X, Y) using fillText()
context.fillText(char, x, y);
//Measure wShorter, the width of the resulting shorter string using measureText().
if (text == "")
wShorter = 0;
else
wShorter = context.measureText(text).width;
//Subtract the width of the shorter string from the width of the entire string, giving the kerned width of the character, wChar = wAll - wShorter
wChar = wAll - wShorter;
//Increment X by wChar + spacing
x += wChar + spacing;
//wAll = wShorter
wAll = wShorter;
//Repeat from step 3
} while (text != "");
}
Code for demo/eyeball test:
element1 = document.getElementById("Test1");
textContext1 = element1.getContext('2d');
textContext1.font = "72px Verdana, sans-serif";
textContext1.textAlign = "left";
textContext1.textBaseline = "top";
textContext1.fillStyle = "#000000";
text = "Welcome to go WAVE";
this.fillTextWithSpacing(textContext1, text, 0, 0, 0);
textContext1.fillText(text, 0, 100);
Ideally I'd throw multiple random strings at it and do a pixel by pixel comparison. I'm also not sure how good Verdana's default kerning is, though I understand it's better than Arial - suggestions on other fonts to try gratefully accepted.
So... so far it looks good. In fact it looks perfect.
Still hoping that someone will point out any flaws in the process.
In the meantime I will put this here for others to see if they are looking for a solution on this.
My answer got deleted.
So, I'm using chrome and here is my complete code.
second_image = $('#block_id').first();
canvas = document.getElementById('canvas');
canvas.style.letterSpacing = '2px';
ctx = canvas.getContext('2d');
canvas.crossOrigin = "Anonymous";
canvasDraw = function(text, font_size, font_style, fill_or_stroke){
canvas.width = second_image.width();
canvas.height = second_image.height();
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(second_image.get(0), 0, 0, canvas.width, canvas.height);
//refill text
ctx.font = font_size +'px '+ font_style + ',Symbola';
$test = ctx.font;
ctx.textAlign = "center";
if(fill_or_stroke){
ctx.fillStyle = "#d2b76d";
ctx.strokeStyle = "#9d8a5e";
ctx.strokeText(text,canvas.width*$left,canvas.height*$top);
ctx.fillText(text,canvas.width*$left,canvas.height*$top);
}
else{
ctx.strokeStyle = "#888888";
ctx.strokeText(text,canvas.width*$left,canvas.height*$top);
}
};
And you don't need to use this function this.fillTextWithSpacing. I didn't use and it worked like a charm)

Finding the actual text height AS3

How can I get the actual height of a text in AS3 TextField? It appears that TextField.textHeight reports some fixed value that doesn't depend on the contents of a TextField.
The example code below produces the following:
text=o p g y d j
textWidth=120.8
textHeight=**96**
text=o
textWidth=15
textHeight=**96**
text=oW
textWidth=43.3
textHeight=**96**
Obviously, the height should be different for "o" and "p", etc.
The AS3 code:
import flash.text.TextField;
var format : TextFormat = new TextFormat();
format.font = "Times New Roman";
format.size = 30;
format.align = TextFormatAlign.CENTER;
var textField1 : TextField = new TextField();
textField1.defaultTextFormat = format;
textField1.selectable = false;
textField1.sharpness = 0;
textField1.embedFonts = true;
textField1.multiline = false;
textField1.height = 50;
textField1.width = 200;
textField1.x = 10;
textField1.y = 10;
addChild(textField1);
textField1.text = "o p g y d j";
trace("text=" + textField1.text);
trace("textWidth=" + textField1.textWidth);
trace("textHeight=" + textField1.textHeight);
textField1.text = "o";
trace("\ntext=" + textField1.text);
trace("textWidth=" + textField1.textWidth);
trace("textHeight=" + textField1.textHeight);
textField1.text = "oW";
trace("\ntext=" + textField1.text);
trace("textWidth=" + textField1.textWidth);
trace("textHeight=" + textField1.textHeight);
stop();
I guess TextField.textHeight is not the right variable, but what should I use instead?
Mark Fox is right that textHeight doesn't represent the actual height of text - and that the classic TextField in Flash doesn't have any support for getting the actual pixel height of rendered text. What textHeight does represent is the line height - which is its ascent (font's height above baseline), descent (font's height below baseline) and leading (space between lines) combined. As implied, the height is constant, based on the font's ascent and descent, not the actual text's. (Bearing this in mind, see Adobe's overview of the terms here - and note that TextLineMetrics won't help you either).
The "new" Flash Text Engine (flash.text.engine) does include properties to get the actual height of text rendered using that technology (e.g. TextLine.totalHeight) - but then we're getting into low level text rendering. If you need to use "classic" TextFields, this won't help you measuring the text anyway, since the Flash Text Engine has its own renderer, which doesn't necessarily render text at the same height and width as "classic" text.
What you can do instead is render the TextField to BitmapData then measure the bounds of the text:
// Create a completely transparent BitmapData:
var bmd:BitmapData = new BitmapData(
textfield.width,
textfield.height,
true,
0x00ffffff);
// Note that some cases may require you to render a Sprite/MovieClip
// CONTAINING the TextField for anything to get drawn.
// For example, AntiAliasType.ADVANCED (antialias for readability) is known to
// not be renderable in some cases - other settings may cause nothing to be
// rendered too. In that case, simply wrap add the TextField as a child of an
// otherwise empty Sprite/MovieClip, and pass that to draw() instead:
bmd.draw(textfield);
// This gets the bounds of pixels that are not completely transparent.
// Param 1: mask = specifies which color components to check (0xAARRGGBB)
// Param 2: color = is the color to check for.
// Param 3: findColor = whether to bound pixels OF the specified color (true),
// or NOT OF the specified color (false)
//
// In this case, we're interested in:
// 1: the alpha channel (0xff000000)
// 2: being 00 (0x00......)
// 3: and want the bounding box of pixels that DON'T meet that criterium (false)
var rect:Rectangle = bmd.getColorBoundsRect(0xff000000, 0x00000000, false);
// Do remember to dispose BitmapData when done with it:
bmd.dispose();
trace("text height = " + rect.height);
trace("text width = " + rect.width);
Note on accuracy
This may be completely irrelevant, depending on what you're going to use this for, but it's worth bearing in mind:
This method will obviously always return a result in whole pixels - the actual rendering of the glyphs, however, uses sub-pixels.
In other words, if you add the results of measuring the width of a "V" and an "o", and then compare that to the result of "Vo", they may not be the same. You might get "40 + 35 = 74". Ignoring that kerning etc. may push the letters closer together, the rendering of each letter (placement, anti-aliasing etc.) may also be different, depending on its context.
Your question highlights a legacy of weak semantics in the Flash typographic APIs.
The textHeight property represents the relative size of the typeface in pixels, it doesn't account for the pixel representation of the particular glyphs (the individual letter shapes) in the TextField.
As far as I know there is no direct programatic way to measure a glyph with Flash. However, you could bitmap the textfield and use getPixel to guess:
var tf:TextField = …your textfield…
var wide:int = tf.width;
var tall:int = tf.height;
var bmpd:BitmapData = new BitmapData(wide, tall, true,0xFFFFFFFF);
bmpd.draw( tf );
var totalPixels:int = wide * tall;
var index:int = totalPixels + 1;
var useIndex:int;
var xPixel:int;
var yPixel:int;
while (--index > 0) {
useIndex = index - 1;
xPixel = useIndex % wide;
yPixel = int(useIndex / wide);
var pixelColor:uint = bmpd.getPixel(xPixel, yPixel);
// write some logic to find the y extremes where the pixelColor values are not white (or whatever background color specified when you created the BitmapData)
}
You can do that by "textHeight";
// Create TextField
var tf:TextField = new TextField();
tf.wordWrap = true;
tf.multiline = true;
tf.selectable = false;
tf.antiAliasType = AntiAliasType.ADVANCED;
tf.autoSize = TextFieldAutoSize.LEFT; // It's should not be "=TextFieldAutoSize.NONE;"
tf.embedFonts = true;
tf.text = "Your text here";
this.addChild(tf);
trace(tf.textHeight);
Hope it will help

Moving background with my cursor

My website's landing page (http://www.chrisamaddeo.com/) has a full screen background. I want to make it move like an example of this website's header (http://www.kaiserair.com/) I have this code, HTML, which goes in the head of my website:
<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script>
$(document).ready(function() {
var movementStrength = 25;
var height = movementStrength / $(window).height();
var width = movementStrength / $(window).width();
$("#top-image").mousemove(function(e){
var pageX = e.pageX - ($(window).width() / 2);
var pageY = e.pageY - ($(window).height() / 2);
var newvalueX = width * pageX * -1 - 50;
var newvalueY = height * pageY * -1 - 25;
$('#top-image').css("background-position", newvalueX+"px "+newvalueY+"px");
});
I have this code, css and it doesn't work:
#top-image {
background:url('https://d3ui957tjb5bqd.cloudfront.net/images/screenshots/products/0/8/8905/red-rocks-park-o.jpg' -50px -25px;
position:fixed ;
top:0;
width:100%;
z-index:0;
}
My website is hosted with weebly and their html is a little different
Actually, i've just tried copying your code:
http://codepen.io/chrisboon27/pen/rEDIC
It does work, in that it moves the image.
I did have to add some some closing braces to your jQuery but maybe you just missed those when you pasted the code into this question.
Also, I looked at your site and noticed you are currently using background-size:cover. This takes your bg image and makes it fit within the div - you dont want that as you want some bg extending beyond the div - so i'n the css in the example I linked to you can see I used calc to make the bg-image size to 100% width + 50px. I used 50px as your code currently moves the background image position by up to 25px left or right, therefore you need it to be 50px total wider than the div.
EDIT:
If you use calc you should include a -webkit prefixed version too (unless you are using prefix-free or prefixr to add prefixes. Here is browser support: http://caniuse.com/calc
If you need to support more browsers you will need to set background-size via javascript
I'm finding that quite hard to follow without recreating it and seeing where those numbers come from. However I recently made something very similar with jQuery for someone else except it moves an img within a div. It probably wouldn't take much to switch it to move a background image though (though finding out the bg-image dimensions would be tricky - you might be better hard coding them in that instance).
html:
<div class="container"><img src="foo.jpg" class="draggable"/>
</div>
jQuery:
//for each item
$(".container").each(function(){
//get the container width
var conWidth = $(this).width();
//and its height
var imgHeight = $(this).find("img").height();
//get the nested img width
var conHeight = $(this).height();
//and its height
var imgWidth = $(this).find("img").width();
//figure out how much of the image is not visible horizontally
var excessWidth = imgWidth - conWidth;
//and how much is not visible vertically
var excessHeight = imgHeight - conHeight;
//how far is this container from the left of the page
var containerPositionLeft = this.offsetLeft;
//and from the top
var containerPositionTop = this.offsetTop;
//when moving mouse over container
$(this).mousemove(function(e){
//figure out how many pixels the mouse is from the left edge of the page
var mouseoffLeftPage = e.pageX;
//and how many from the top edge of the page
var mouseoffTopPage = e.pageY;
//figure out how many pixels the mouse is from the left edge of the page
var mouseoffLeftPx = mouseoffLeftPage - containerPositionLeft;
//and how many from the top edge of the page
var mouseoffTopPx = mouseoffTopPage - containerPositionTop;
//figure out the distance the mouse is from the left edge as a percentage (kind of - all the way to the right equals 1 not 100)
var mouseoffLeftPercent = mouseoffLeftPx/conWidth;
//do the same for height
var mouseoffTopPercent = mouseoffTopPx/conHeight;
//times the 'percentage' value by the amount of image hidden - so if your conatiner is 200px wide, your image 300px wide nd your mouse is half way across this value would be 0.5*100 which would give you 50 - which is exactly half the amount of image that is missing.
//note this gets set as a minus value as we will be using a minus number to shift the image around.
var setnewWidth = -(mouseoffLeftPercent * excessWidth);
//do the same for the height
var setnewHeight = -(mouseoffTopPercent * excessHeight);
//add the values as css (using transform(translate) as it's more performant and does subpixel rendering so looks smoother [or does it? it does in animations but seems as my js is not technically animating it it might not make a difference in that respect] - could set the top,left version as fallback for unsupporting browsers but ie9 supports transforms anyway so i dont care.)
$(this).find("img").css({"transform" : "translate("+ setnewWidth+"px ,"+setnewHeight+"px)" });
//$(this).find("img").css({"left" : setnewWidth+"px", "top" : setnewHeight+"px" });
});
});
Not a direct answer to what isn't working in your code, but shows an example (with comments on what is happening) of how it can be done - note that my version doesn't rely on you knowing any of the widths or heights of objects and can run on multiple items on one page - also it doesn't have to be placed at the very top of the page. It does assume the image is larger than its container though - if it isn't larger the image just moves around within it.
You could reduce the number of variables by doing more calculations in a row, I just wanted it to be as easy to read the logic as possible.
DEMO:
http://codepen.io/chrisboon27/pen/BhkJq
Simpler than that, you can just make sure the CSS position is set to "fixed" so wherever you are on the page it's always 0px from the top.