Programmatically measure text string in pixels for Silverlight - text

In WPF there is the FormattedText in the System.Windows.Media namespace MSDN FormattedText that I can use like so:
private static Size GetTextSize(string txt, string font, int size, bool isBold)
{
Typeface tf = new Typeface(new System.Windows.Media.FontFamily(font),
FontStyles.Normal,
(isBold) ? FontWeights.Bold : FontWeights.Normal,
FontStretches.Normal);
FormattedText ft = new FormattedText(txt, new CultureInfo("en-us"), System.Windows.FlowDirection.LeftToRight, tf, (double)size, System.Windows.Media.Brushes.Black, null, TextFormattingMode.Display);
return new Size { Width = ft.WidthIncludingTrailingWhitespace, Height = ft.Height };
}
Is there a good approach in Silverlight to getting the width in pixels (at the moment height isn't important) besides making a call to the server?

An approach that I've seen used, that may not work in your particular instance, is to throw the text into an unstyled TextBlock, and then get the width of that control, like so:
private double GetTextWidth(string text, int fontSize)
{
TextBlock txtMeasure = new TextBlock();
txtMeasure.FontSize = fontSize;
txtMeasure.Text = text;
double width = txtMeasure.ActualWidth;
return width;
}
It's a hack, no doubt.

Related

How to resize a SVG element composed of multiple paths in JavaFX?

I'm remaking the design of an old javafx app and I need to include an icon for the wifi strength. The designer sent me an svg icon for that, and I thought it was smarter to just keep one icon and use different fill colors for the 3 bars depending of the said wifi strength.
I found a nice approach for that, and it works quite well until you need to resize the wifi icon. It seems like even if I change the -fx-pref-width (or height, or min/max) the svg icon keeps its size.
I also tried to resize each svg path one by one but it only goes messy with the spaces between them. And using a unique shape to resize later is not an option, as I need at least 2 colors. FYI the goal is to apply a 5em or 3em size depending of the context.
Here's the code I currently have, where everything looks great if you don't matter the icon size :
public class WifiStrengthRegion extends Pane {
public WifiStrengthRegion() {
getStyleClass().setAll("wifi-strength");
SVGPath round = new SVGPath();
SVGPath bar1 = new SVGPath();
SVGPath bar2 = new SVGPath();
SVGPath bar3 = new SVGPath();
round.setContent("M253.5,336.5c-18.9,0-34.2,15.3-34.2,34.1c0,18.8,15.4,34.1,34.2,34.1c18.9,0,34.2-15.3,34.2-34.1 C287.7,351.8,272.4,336.5,253.5,336.5z");
bar1.setContent("M337,290.1c-22.3-22.3-51.9-34.5-83.5-34.5c-31.4,0-61,12.2-83.3,34.3c-9,9-9,23.5,0,32.5 c4.4,4.4,10.2,6.8,16.3,6.8c6.2,0,11.9-2.4,16.3-6.7c13.6-13.5,31.6-20.9,50.7-20.9c19.2,0,37.3,7.5,50.8,21 c4.4,4.4,10.2,6.8,16.3,6.8c6.2,0,11.9-2.4,16.3-6.7C346,313.6,346,299,337,290.1z");
bar2.setContent("M389.3,238c-36.3-36.2-84.5-56.1-135.8-56.1c-51.2,0-99.3,19.9-135.6,55.9c-4.4,4.3-6.8,10.1-6.8,16.3 c0,6.1,2.4,11.9,6.7,16.3c4.4,4.3,10.2,6.7,16.3,6.7c6.2,0,11.9-2.4,16.3-6.7c27.5-27.4,64.1-42.5,103-42.5 c39,0,75.6,15.1,103.1,42.6c4.4,4.4,10.2,6.8,16.3,6.8c6.2,0,12-2.4,16.3-6.7c4.4-4.3,6.8-10.1,6.8-16.3 C396,248.1,393.6,242.3,389.3,238z");
bar3.setContent("M444.3,183.2c-50.9-50.8-118.7-78.8-190.8-78.8c-72,0-139.7,27.9-190.6,78.6c-9,9-9,23.5,0,32.5 c4.4,4.3,10.2,6.7,16.3,6.7c6.2,0,12-2.4,16.3-6.7c42.2-42,98.3-65.2,158-65.2c59.7,0,115.9,23.2,158.1,65.3 c4.4,4.3,10.2,6.7,16.3,6.7c6.2,0,11.9-2.4,16.3-6.7C453.2,206.7,453.3,192.1,444.3,183.2z");
round.getStyleClass().add("wifi-base");
bar1.getStyleClass().add("wifi-bar1");
bar2.getStyleClass().add("wifi-bar2");
bar3.getStyleClass().add("wifi-bar3");
this.getChildren().addAll(round, bar1, bar2, bar3);
}
public void setWifiStrength(Integer strength) {
if (strength == null) {
setManaged(false);
setVisible(false);
} else {
setManaged(true);
setVisible(true);
getStyleClass().removeAll("wifi-excellent", "wifi-good", "wifi-fair", "wifi-weak", "wifi-off");
if (strength < 0 && strength >= -100) {
if (strength >= -50) {
getStyleClass().add("wifi-excellent");
} else if (strength >= -70) {
getStyleClass().add("wifi-good");
} else if (strength >= -80) {
getStyleClass().add("wifi-fair");
} else {
getStyleClass().add("wifi-weak");
}
} else {
getStyleClass().add("wifi-off");
}
}
}
}
(and then a css stylesheet applies -fx-fill to each .wifi-barX depending of the main element class)
And here is an example of how the svg icon looks like:
I'm a very beginner in Java (and obv JavaFX), so any constructive criticism will be appreciated!
If you want to resize only (I hope this works):
public void resize(SVGPath svg, double width, double height) {
this.width = width;
this.height = height;
double originalWidthR = svg.prefWidth(-1);
double originalHeightR = svg.prefHeight(originalWidthR);
double scaleXr = width / originalWidthR;
double scaleYr = height / originalHeightR;
svg.setScaleX(scaleXr);
svg.setScaleY(scaleYr);
}
And if you want to set bounds use this:
public void setBounds(SVGPath svg, double width, double height, double x, double y) {
this.width = width;
this.height = height;
double originalWidthR = svg.prefWidth(-1);
double originalHeightR = svg.prefHeight(originalWidthR);
double scaleXr = width / originalWidthR;
double scaleYr = height / originalHeightR;
svg.setScaleX(scaleXr);
svg.setScaleY(scaleYr);
svg.setLayoutX((originalWidthR - width) + x);
svg.setLayoutY((originalHeightR - height) + y);
}

Placing canvas inside Div element

I am trying to create a circle in itext 7 and then place this circle anywhere in I need to in the document.
The document is laid out using divs and I have managed to create the circle using a PdfCanvas.
Below is a snippet of what I am trying to achieve and there may well be a better way to do this:
PdfDocument pdfDoc = new PdfDocument(writer);
Document doc = new Document(pdfDoc);
PdfPage pdfPage = pdfDoc.addNewPage();
Div div = new Div();
div.setBackgroundColor(Color.CYAN);
div.setHeight(10.0F);
div.add(new Paragraph(" ").setFont(PdfFontFactory.createFont(FontConstants.COURIER_BOLD)));
doc.add(div);
PdfCanvas canvas = new PdfCanvas(pdfPage);
Color white = Color.WHITE;
Color black = Color.BLACK;
canvas.setColor(white, true)
.setStrokeColor(black)
.circle(15, 800, 8)
.fillStroke();
canvas.beginText()
.setFontAndSize(PdfFontFactory.createFont(FontConstants.COURIER_BOLD), 10)
.setColor(black, true)
.moveText(15 - 3, 800 - 3)
.showText("1")
.endText();
doc.close();
If there is a correct way of wrapping some text (number) in a circle that can be positioned inside a div then I will happily change to this method if someone can point me towards a tutorial or some documentation I can follow.
If anybody is looking for an easy way to do this then you can override the draw method of Div.
public class Circle extends Div
{
#Override
public IRenderer getRenderer()
{
return new CircleRenderer(this);
}
private class CircleRenderer extends DivRenderer
{
public CircleRenderer(final Circle circle)
{
super(circle);
setPadding(PADDING);
}
#Override
public void draw(final DrawContext drawContext)
{
final PdfCanvas canvas = drawContext.getCanvas();
final Rectangle area = this.occupiedArea.getBBox();
final float x = area.getX();
final float y = area.getY();
canvas.circle(x, y, 8);
canvas.fillStroke();
super.draw(drawContext);
}
}
}
After I draw the canvas I need to do some correction of the x and y positions and same for any text placed inside the circle.

Trying to draw Rotated text with CGAffineTransform and MakeRotation appears at wrong location

I'm trying to draw some rotated texts by using the CGAffineTransform.MakeRotation method at specifc location. I also make use of the TranslateCTM, but something must be wrong as rotated texts do not appear aligned and at the correct x, y position where they should appear, here is simple the code I'm using, anyone know where the problem is? :
public override void Draw (RectangleF rect)
{
DrawTextRotated("Hello1",10,100,30);
DrawTextRotated("Hello2",50,100,60);
SetNeedsDisplay();
}
static public float DegreesToRadians(float x)
{
return (float) (Math.PI * x / 180.0);
}
public void DrawTextRotated(string text,int x, int y, int rotDegree)
{
CGContext c = UIGraphics.GetCurrentContext();
c.SaveState();
c.TextMatrix = CGAffineTransform.MakeRotation((float)DegreesToRadians((float)(-rotDegree)));
c.ConcatCTM(c.TextMatrix);
float xxx = ((float)Math.Sin(DegreesToRadians((float)rotDegree))*y);
float yyy = ((float)Math.Sin(DegreesToRadians((float)rotDegree))*x);
// Move the context back into the view
c.TranslateCTM(-xxx,yyy);
c.SetTextDrawingMode(CGTextDrawingMode.Fill);
c.SetShouldSmoothFonts(true);
MonoTouch.Foundation.NSString str = new MonoTouch.Foundation.NSString(text);
SizeF strSize = new SizeF();
strSize = str.StringSize(UIFont.SystemFontOfSize(12));
RectangleF tmpR = new RectangleF(x,y,strSize.Width,strSize.Height);
str.DrawString(tmpR,UIFont.SystemFontOfSize(12),UILineBreakMode.WordWrap,UITextAlignment.Right);
c.RestoreState();
}
Thanks !
Here's some code that will draw text rotated properly about the top-left corner of the text. For the moment, I'm disregarding your use of text alignment.
First, a utility method to draw a marker where we expect the text to show up:
public void DrawMarker(float x, float y)
{
float SZ = 20;
CGContext c = UIGraphics.GetCurrentContext();
c.BeginPath();
c.AddLines( new [] { new PointF(x-SZ,y), new PointF(x+SZ,y) });
c.AddLines( new [] { new PointF(x,y-SZ), new PointF(x,y+SZ) });
c.StrokePath();
}
And the code to draw the text (note I've replaced all int rotations with float, and you may want negate your rotation):
public void DrawTextRotated(string text, float x, float y, float rotDegree)
{
CGContext c = UIGraphics.GetCurrentContext();
c.SaveState();
DrawMarker(x,y);
// Proper rotation about a point
var m = CGAffineTransform.MakeTranslation(-x,-y);
m.Multiply( CGAffineTransform.MakeRotation(DegreesToRadians(rotDegree)));
m.Multiply( CGAffineTransform.MakeTranslation(x,y));
c.ConcatCTM( m );
// Draws text UNDER the point
// "This point represents the top-left corner of the string’s bounding box."
//http://developer.apple.com/library/ios/#documentation/UIKit/Reference/NSString_UIKit_Additions/Reference/Reference.html
NSString ns = new NSString(text);
UIFont font = UIFont.SystemFontOfSize(12);
SizeF sz = ns.StringSize(font);
RectangleF rect = new RectangleF(x,y,sz.Width,sz.Height);
ns.DrawString( rect, font);
c.RestoreState();
}
Rotation about a point requires translation of the point to the origin followed by rotation, followed by rotation back to the original point. CGContext.TextMatrix has no effect on NSString.DrawString so you can just use ConcatCTM.
The alignment and line break modes don't have any effect. Since you're using NSString.StringSize, the bounding rectangle fits the entirety of the text, snug up against the left and right edges. If you make the width of the bounding rectangle wider and use UITextAlignment.Right, you'll get proper right alignment, but the text will still rotate around the top left corner of the entire bounding rectangle. Which is not, I'm guessing, what you're expecting.
If you want the text to rotate around the top right corner, let me know and I'll adjust the code accordingly.
Here's the code I used in my test:
DrawTextRotated("Hello 0",100, 50, 0);
DrawTextRotated("Hello 30",100,100,30);
DrawTextRotated("Hello 60",100,150,60);
DrawTextRotated("Hello 90",100,200,90);
Cheers.

I am beginner in j2me.In J2me Ticker function, How to apply differnt Color in Single Ticker?

*I am developing one j2me-Lwuit Project for Nokia s40 devices.I have some problem abuot ticker. I have apply Only one color for tiker.But i want differnt color to apply for single ticker.This is my code for Ticker:
Ticker tick;
String tickerText=" ";
Label lblIndice=new Label();
Label ticker=new Label("");
for (int i = 0; i < tickerIndiceData.size(); i++)
{
tickerText +=" "+tickerIndiceData.elementAt(i).toString();
tickerText +=" "+tickerValueData.elementAt(i).toString();
tickerText +=" "+"("+tickerChangeData.elementAt(i).toString()+")";
lblIndice.setText(" "+tickerIndiceData.elementAt(i).toString());
lblValue.setText(" "+tickerValueData.elementAt(i).toString());
double val=Double.parseDouble(tickerChangeData.elementAt(i).toString());
if(val>0)
{
ticker.getStyle().setFgColor(0X2E9F37);
}
else
{
ticker.getStyle().setFgColor(0XFF0000);
}
lblChange.setText(" "+"("+val+")");
}
System.out.println("TICKER==="+tickerText);
ticker.setText(tickerText);
ticker.getStyle().setFont(Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_SMALL));
ticker.startTicker(50, true);*
LWUIT doesn't support different colors for a label (hence ticker) since that would require quite a bit of processing.
Implementing a ticker from scratch in LWUIT is pretty easy though. Just derive label and override paint as such:
public void paint(Graphics g) {
UIManager.getInstance().setFG(g, this);
Style style = l.getStyle();
Font f = style.getFont();
boolean isTickerRunning = l.isTickerRunning();
int txtW = f.stringWidth(text);
// update this to draw two strings one with the color that's already set and the
// other with the color you want
g.drawString(getText(), getShiftText() + getX(), getY(),style.getTextDecoration());
}

BlackBerry - image 3D transform

I know how to rotate image on any angle with drawTexturePath:
int displayWidth = Display.getWidth();
int displayHeight = Display.getHeight();
int[] x = new int[] { 0, displayWidth, displayWidth, 0 };
int[] x = new int[] { 0, 0, displayHeight, displayHeight };
int angle = Fixed32.toFP( 45 );
int dux = Fixed32.cosd(angle );
int dvx = -Fixed32.sind( angle );
int duy = Fixed32.sind( angle );
int dvy = Fixed32.cosd( angle );
graphics.drawTexturedPath( x, y, null, null, 0, 0, dvx, dux, dvy, duy, image);
but what I need is a 3d projection of simple image with 3d transformation (something like this)
Can you please advice me how to do this with drawTexturedPath (I'm almost sure it's possible)?
Are there any alternatives?
The method used by this function(2 walk vectors) is the same as the oldskool coding tricks used for the famous 'rotozoomer' effect. rotozoomer example video
This method is a very fast way to rotate, zoom, and skew an image. The rotation is done simply by rotating the walk vectors. The zooming is done simply by scaling the walk vectors. The skewing is done by rotating the walkvectors in respect to one another (e.g. they don't make a 90 degree angle anymore).
Nintendo had made hardware in their SNES to use the same effect on any of the sprites and or backgrounds. This made way for some very cool effects.
One big shortcoming of this technique is that one can not perspectively warp a texture. To do this, every new horizontal line, the walk vectors should be changed slightly. (hard to explain without a drawing).
On the snes they overcame this by altering every scanline the walkvectors (In those days one could set an interrupt when the monitor was drawing any scanline). This mode was later referred to as MODE 7 (since it behaved like a new virtual kind of graphics mode). The most famous games using this mode were Mario kart and F-zero
So to get this working on the blackberry, you'll have to draw your image "displayHeight" times (e.g. Every time one scanline of the image). This is the only way to achieve the desired effect. (This will undoubtedly cost you a performance hit since you are now calling the drawTexturedPath function a lot of times with new values, instead of just one time).
I guess with a bit of googling you can find some formulas (or even an implementation) how to calc the varying walkvectors. With a bit of paper (given your not too bad at math) you might deduce it yourself too. I've done it myself too when I was making games for the Gameboy Advance so I know it can be done.
Be sure to precalc everything! Speed is everything (especially on slow machines like phones)
EDIT: did some googling for you. Here's a detailed explanation how to create the mode7 effect. This will help you achieve the same with the Blackberry function. Mode 7 implementation
With the following code you can skew your image and get a perspective like effect:
int displayWidth = Display.getWidth();
int displayHeight = Display.getHeight();
int[] x = new int[] { 0, displayWidth, displayWidth, 0 };
int[] y = new int[] { 0, 0, displayHeight, displayHeight };
int dux = Fixed32.toFP(-1);
int dvx = Fixed32.toFP(1);
int duy = Fixed32.toFP(1);
int dvy = Fixed32.toFP(0);
graphics.drawTexturedPath( x, y, null, null, 0, 0, dvx, dux, dvy, duy, image);
This will skew your image in a 45º angle, if you want a certain angle you just need to use some trigonometry to determine the lengths of your vectors.
Thanks for answers and guidance, +1 to you all.
MODE 7 was the way I choose to implement 3D transformation, but unfortunately I couldn't make drawTexturedPath to resize my scanlines... so I came down to simple drawImage.
Assuming you have a Bitmap inBmp (input texture), create new Bitmap outBmp (output texture).
Bitmap mInBmp = Bitmap.getBitmapResource("map.png");
int inHeight = mInBmp.getHeight();
int inWidth = mInBmp.getWidth();
int outHeight = 0;
int outWidth = 0;
int outDrawX = 0;
int outDrawY = 0;
Bitmap mOutBmp = null;
public Scr() {
super();
mOutBmp = getMode7YTransform();
outWidth = mOutBmp.getWidth();
outHeight = mOutBmp.getHeight();
outDrawX = (Display.getWidth() - outWidth) / 2;
outDrawY = Display.getHeight() - outHeight;
}
Somewhere in code create a Graphics outBmpGraphics for outBmp.
Then do following in iteration from start y to (texture height)* y transform factor:
1.create a Bitmap lineBmp = new Bitmap(width, 1) for one line
2.create a Graphics lineBmpGraphics from lineBmp
3.paint i line from texture to lineBmpGraphics
4.encode lineBmp to EncodedImage img
5.scale img according to MODE 7
6.paint img to outBmpGraphics
Note: Richard Puckett's PNGEncoder BB port used in my code
private Bitmap getMode7YTransform() {
Bitmap outBmp = new Bitmap(inWidth, inHeight / 2);
Graphics outBmpGraphics = new Graphics(outBmp);
for (int i = 0; i < inHeight / 2; i++) {
Bitmap lineBmp = new Bitmap(inWidth, 1);
Graphics lineBmpGraphics = new Graphics(lineBmp);
lineBmpGraphics.drawBitmap(0, 0, inWidth, 1, mInBmp, 0, 2 * i);
PNGEncoder encoder = new PNGEncoder(lineBmp, true);
byte[] data = null;
try {
data = encoder.encode(true);
} catch (IOException e) {
e.printStackTrace();
}
EncodedImage img = PNGEncodedImage.createEncodedImage(data,
0, -1);
float xScaleFactor = ((float) (inHeight / 2 + i))
/ (float) inHeight;
img = scaleImage(img, xScaleFactor, 1);
int startX = (inWidth - img.getScaledWidth()) / 2;
int imgHeight = img.getScaledHeight();
int imgWidth = img.getScaledWidth();
outBmpGraphics.drawImage(startX, i, imgWidth, imgHeight, img,
0, 0, 0);
}
return outBmp;
}
Then just draw it in paint()
protected void paint(Graphics graphics) {
graphics.drawBitmap(outDrawX, outDrawY, outWidth, outHeight, mOutBmp,
0, 0);
}
To scale, I've do something similar to method described in Resizing a Bitmap using .scaleImage32 instead of .setScale
private EncodedImage scaleImage(EncodedImage image, float ratioX,
float ratioY) {
int currentWidthFixed32 = Fixed32.toFP(image.getWidth());
int currentHeightFixed32 = Fixed32.toFP(image.getHeight());
double w = (double) image.getWidth() * ratioX;
double h = (double) image.getHeight() * ratioY;
int width = (int) w;
int height = (int) h;
int requiredWidthFixed32 = Fixed32.toFP(width);
int requiredHeightFixed32 = Fixed32.toFP(height);
int scaleXFixed32 = Fixed32.div(currentWidthFixed32,
requiredWidthFixed32);
int scaleYFixed32 = Fixed32.div(currentHeightFixed32,
requiredHeightFixed32);
EncodedImage result = image.scaleImage32(scaleXFixed32, scaleYFixed32);
return result;
}
See also
J2ME Mode 7 Floor Renderer - something much more detailed & exciting if you writing a 3D game!
You want to do texture mapping, and that function won't cut it. Maybe you can kludge your way around it but the better option is to use a texture mapping algorithm.
This involves, for each row of pixels, determining the edges of the shape and where on the shape those screen pixels map to (the texture pixels). It's not so hard actually but may take a bit of work. And you'll be drawing the pic only once.
GameDev has a bunch of articles with sourcecode here:
http://www.gamedev.net/reference/list.asp?categoryid=40#212
Wikipedia also has a nice article:
http://en.wikipedia.org/wiki/Texture_mapping
Another site with 3d tutorials:
http://tfpsly.free.fr/Docs/TomHammersley/index.html
In your place I'd seek out a simple demo program that did something close to what you want and use their sources as base to develop my own - or even find a portable source library, I´m sure there must be a few.

Resources