Determining string/font dimensions - android

I have a method that is passed a text string. For a specified font and size, it works out the bounding box for the text and then draws a roundrect that will contain it.
I'm trying to keep all of these roundrects the same height (irrespective of ascenders and descenders in the font), so the basic getTextBounds isn't quite enough as it bounds the specific text passed.
The solution I came up with was to use getTextBounds to work out the width of the text, and ascent/descent from FontMetricsInt to work out the height:
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setTextSize(fontsize);
paint.setTypeface(Typeface.SANS_SERIF);
Rect textBounds = new Rect();
paint.getTextBounds(value, 0, value.length(), textBounds);
FontMetricsInt fm = paint.getFontMetricsInt();
this.width = textBounds.width();
this.height = fm.descent - fm.ascent;
this.textAscent = fm.ascent;
Is there a simpler way?
Edits: Finally figured out how to format the code properly!

Why don't you use use direct function like: paint.measureText(value), paint.ascent(), paint.descent()? The code would be:
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setTextSize(fontsize);
paint.setTypeface(Typeface.SANS_SERIF);
this.width = paint.measureText(value);
this.height = paint.descent() - paint.ascent();
this.textAscent = paint.ascent();
Not a big improvement, but easier to read.

You don't need to subtract ascent() from descent(). You can just use Paint.getFontSpacing() :
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setTextSize(fontsize);
paint.setTypeface(Typeface.SANS_SERIF);
this.width = paint.measureText(value);
this.height = paint.getFontSpacing();
this.textAscent = paint.ascent();

Related

Android - Any library for adding repost label to bitmap

I have been trying to make a photo sharing app, with the ability to add your image and name to the image. I have been messing with Canvas for the whole day, but couldn't get good results. I was able to draw the name and bitmap, but they didn't look so good.
That's why I am here asking about is there any library or piece of code that could help me in making something similar to [this][1]. I wasn't able to find any thing for it.
EDIT: Sorry for not adding my own code
Here is my code from my latest try
public void AddText(Position2D pos){
//Position2D is an enum having the 4 corners of the image
bmWorking= bmOriginal.copy(Bitmap.Config.ARGB_8888,true);
Canvas canvas = new Canvas(bmWorking);
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
Paint textPaint = new Paint();
textPaint.setColor(Color.BLACK);
float width = (35f/100f) * bmWorking.getWidth();
float height = (width/16f) * 3;
textPaint.setTextSize(height - 4); //I wanted to have some space (margin) above and below the text
textPaint.setTextAlign(Paint.Align.LEFT);
float [] coords = getPositionCoords(pos, width, height); //getPositionCoords returns a float array with the Left,Top,Right,Bottom position calculated based on the width and height
canvas.drawRect(coords[0],coords[1], coords[2], coords[3],paint);
username = "Haider Ali Punjabi";
canvas.drawText(username, coords[0] ,coords[3], textPaint);
bitmapView.setImageBitmap(bmWorking);
}
Here is the result
UPDATE:
#pskink gave me this code
which works nicely
if you want to customize it, then instead of solid white rectangle (like in your original code) use a Drawable and the result could be something like this:
the code:
// for int gravity: see android.view.Gravity, like Gravity.LEFT, Gravity.BOTTOM, etc
// for example:
// Bitmap out = addText(this, in, "Haider Ali Punjabi", android.R.drawable.alert_light_frame, Gravity.BOTTOM, new Point(10, 10));
public Bitmap addText(Context ctx, Bitmap in, String text, int resId, int gravity, Point pad) {
if (pad == null) pad = new Point();
Bitmap out = in.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(out);
Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(Color.BLACK);
textPaint.setTextAlign(Paint.Align.LEFT);
// textPaint.setTextSize(128);
Rect inBounds = new Rect();
textPaint.getTextBounds(text, 0, text.length(), inBounds);
float scale = out.getWidth() * 0.35f / inBounds.width();
Rect container = new Rect(0, 0, out.getWidth(), out.getHeight());
Rect outBounds = new Rect();
int w = (int) (inBounds.width() * scale);
int h = (int) (inBounds.height() * scale);
Gravity.apply(gravity, 2 * pad.x + w, 2 * pad.y + h, container, outBounds);
Drawable dr = ctx.getResources().getDrawable(resId);
Rect padding = new Rect();
dr.getPadding(padding);
dr.setBounds(outBounds.left - padding.left, outBounds.top - padding.top, outBounds.right + padding.right, outBounds.bottom + padding.bottom);
dr.draw(canvas);
Matrix matrix = new Matrix();
RectF src = new RectF(inBounds);
RectF dst = new RectF(outBounds);
dst.inset(pad.x, pad.y);
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
canvas.concat(matrix);
canvas.drawText(text, 0, 0, textPaint);
return out;
}

Create Bitmap using a String

I want to create a Bitmap using the String. The problem is when I assign the Paint and String to the Canvas.
All I see is a dot/black pixel that is created is something wrong with the Configs that I am using?
Here is my code below:
private void createBitmap(){
int textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, getApplicationContext().getResources().getDisplayMetrics());
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setSubpixelText(true);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(textSize);
paint.setColor(Color.BLACK);
int w = 500, h = 200;
Bitmap.Config conf = Bitmap.Config.ARGB_8888; // see other conf types
Bitmap myBitmap = Bitmap.createBitmap(w, h, conf);
Canvas myCanvas = new Canvas(myBitmap);
myCanvas.drawColor(Color.WHITE, PorterDuff.Mode.CLEAR);
myCanvas.drawText("Just a string", 0, 0, paint);
imageView = new ImageView(this);
imageView.setImageBitmap(myBitmap);
}
The y parameter is actually for the baseline of the text, so you won't really see anything with y == 0. The dot you're seeing is probably the descender of the "g" in "string".
Try changing to
myCanvas.drawText("Just a string", 0, 100, paint);
so at least you can see something.
Note: You are setting the text size based on density, but you are making the bitmap an absolute pixel size, so you are going to have to do some calculation to get the look you want.
Once you have your Paint configured, you can determine the height of the text in pixels by calling getFontMetrics() on the Paint, then looking at the FontMetrics values. ascent will be negative since it's measuring upward, so you can get a rough idea of the height by fm.descent - fm.ascent.
Here's a way to draw your text just below the top edge of the bitmap:
Paint.FontMetrics fm = paint.getFontMetrics();
int baseline = (int) - fm.ascent; // also fm.top instead of fm.ascent
myCanvas.drawText("Just a string", 0, baseline, paint);

Canvas drawtext positioning

I am creating a drawing tool, where user add text to image. While making the text draw to bitmap via canvas position is not being set properly.
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
paint.setTypeface(tf);
paint.setTextAlign(Paint.Align.LEFT);
paint.setTextSize(30);
int xPos = layoutTextViewContainer.getLeft();
int yPos = layoutTextViewContainer.getTop();
canvas.drawText(text, xPos, yPos, paint);
Paint
Rect textRect = new Rect();
paint.getTextBounds(text, 0, text.length(), textRect);
textRect.offset(0, -textRect.top);
Canvas canvas = new Canvas(bm);
layoutTextViewContainer holds the edit text.
Screen shot for more clarification.
Black text is written and Red text is preview of embed in image
Got the solution. the values should be pixel independent
convert xPos and yPos as below before passing to drawText
xPos = (int) (xPos / getResources().getDisplayMetrics().density);
yPos = (int) (yPos / getResources().getDisplayMetrics().density);
Its even better to use Drawing cache and save that to any file location.
In this case we dont have to much bother about positioning.
Wrap all in one layout and get the
view.getDrawingCache()

measuring text on scaled canvas

I've been struggling with text measuring and scaled canvases.
When the canvas is unscaled, getTextBounds and measureText deliver accurate results. However, when the canvas is scaled both methods do not deliver results that match the actual size of a printed text.
For testing I've created a subclass of View with the following onDraw method:
final float scaling = 0.51f;
final int fontSize = 50;
canvas.scale(scaling, scaling);
font = Typeface.create("Arial", Typeface.NORMAL);
Paint paint = new Paint();
paint.setColor(0xff4444ff);
paint.setTypeface(font);
paint.setTextSize(fontSize);
paint.setAntiAlias(true);
int x = 10;
int y = 100;
final String text = "Lorem ipsum dolor sit amet, consectetur adipisici elit...";
canvas.drawText(text, x, y, paint);
// draw border using getTextBounds
paint.setColor(0xffff0000);
paint.setStyle(Paint.Style.STROKE);
paint.setTypeface(font);
paint.setTextSize(fontSize);
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
bounds.offset(x, y);
paint.setColor(0x80ffff00);
canvas.drawRect(bounds, paint);
// draw border using measureText
float w = paint.measureText(text);
bounds.left = x;
bounds.right = (int) Math.ceil(bounds.left + w);
bounds.top -= 10;
bounds.bottom += 10;
paint.setColor(0x8000ffff);
paint.setPathEffect(new DashPathEffect(new float[] { 10, 10 }, 0));
canvas.drawRect(bounds, paint);
for scaling = 0.5 I get the following output:
for scaling = 0.51 the following result is shown:
The yellow solid border marks the rect delivered from getTextBounds, the dashed cyan rect is rendered using the width delivered from measureText.
As you can see, the text with scaling = 0.5 is smaller than the measured dimensions and with scaling=0.51 the drawn text is way bigger than the measured dimension.
Any help is appreciated!
Ok, just found out how to circumvent the issue.
The problem is that the Paint does not know about the Canvas scaling. Therefore measureText and getTextBounds deliver the unscaled result. But since the font size does not scale linearly (however, the drawn rect does ), you have to make up for that effect manually.
So the solution would be:
// paint the text as usual
Paint paint = new Paint();
paint.setTypeface(font);
paint.setTextSize(fontSize);
canvas.drawText(text, x, y, paint);
// measure the text using scaled font size and correct the scaled value afterwards
Paint paint = new Paint();
paint.setTypeface(font);
paint.setTextSize(fontSize * scaling);
float w = paint.measureText(text) / scaling;
Using Mono for Android I had to use display metrics as shown here:
public override System.Drawing.SizeF MeasureString(MyFont f, string text)
{
Rect r = new Rect();
f.DrawingFont.GetTextBounds(text, 0, text.Length, r);
//Manual scaling using DisplayMetrics due to Android
//issues for compatibility with older versions
Android.Util.DisplayMetrics metrics = new Android.Util.DisplayMetrics();
GetDisplay.GetMetrics(metrics);
return new System.Drawing.SizeF(r.Width(), r.Height() * metrics.Density);
}
Where f.DrawingFont is an Androdid.Text.TextPaint GetDisplay is:
private Display GetDisplay()
{
return this.GetSystemService(Android.Content.Context.WindowService).JavaCast<Android.Views.IWindowManager>().DefaultDisplay;
}
And the same method in Java is:
private Display getDisplay() {
return ((WindowManager) getContext().getSystemService(
Context.WINDOW_SERVICE)).getDefaultDisplay();
}

Not getting complete text while Animation on Canvas : Android,

I want to get an animated text in Android where it should run from left to right on the screen.
Eg.
I want to get this text
private static final String QUOTE =
"Nobody uses Java anymore. It's this big heavyweight ball and chain.";
running from right to left using animation.
However, this text gets truncated to "Nobody uses Java anymore. It's" in Portrait mode and to
"Nobody uses Java anymore. It's this big heavyweight" in landscape mode.
This is the code that I have used
Paint paint = new Paint();
paint.setColor(Color.BLUE);
private static final String QUOTE =
"Nobody uses Java anymore. It's this big heavyweight ball and chain.";
paint.setTextSize(20);
paint.setAntiAlias(true);
int len= QUOTE.length();
canvas.scale(1,-1);
canvas.drawText(QUOTE, 0, len-1, 100, 60, paint);
canvas.drawText(QUOTE,0,100 , paint);
createAnim(canvas);
private void createAnim(Canvas canvas) {
anim2= new TranslateAnimation(500,-500,30,30);
anim2.setRepeatCount(Animation.INFINITE);
anim2.setInterpolator(new AccelerateDecelerateInterpolator());
anim2.setRepeatMode(Animation.RESTART);
anim2.setDuration(1000L);
startAnimation(anim2);
}
How to do Text Animations using Android SDK?
Please refer this post for text animations.
Please check height and width of canvas ,
// Custom Font Text
Bitmap bitmap = Bitmap.createBitmap((int) getWindowManager()
.getDefaultDisplay().getWidth(),
(int) getWindowManager().getDefaultDisplay()
.getHeight() / 2, Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
drawingImageView.setImageBitmap(bitmap);
paint = new Paint();
paint.setColor(Color.BLACK);
paint.setTextSize(150);
Typeface chops = Typeface.createFromAsset(this.getAssets(),
"WC_Speed_Bold_Bta.ttf");
paint.setTypeface(chops);
int xPos = (canvas.getWidth() / 2);
int yPos = (int) ((canvas.getHeight() / 2) - ((paint.descent() + paint
.ascent()) / 2));
canvas.drawText("Hello", xPos, yPos, paint);

Categories

Resources