0

When I call the function canvas.drawText() in my custom view,I got strange result,like this:

protected void onDraw(Canvas canvas) {

    super.onDraw(canvas);
    canvas.translate(50, 50);
    mPaint.setTextSize(60);

    String str = "helloworld";
    float[] wids = new float[10];
    mPaint.getTextWidths(str, wids);
    float width = 0;
    for (int j = 0; j < wids.length; j++) {
        String string = String.valueOf(str.charAt(j));
        canvas.drawText(string, width, 50, mPaint);   //draw by characters
        width = width + mPaint.measureText(string);   //the start X
    }
}

and this:

protected void onDraw(Canvas canvas) {

    super.onDraw(canvas);
    canvas.translate(50, 50);
    mPaint.setTextSize(60);
    String str = "helloworld";
    canvas.drawText(str, 0, 50, mPaint); // draw by strings
    }

why the two methods run different? I need draw by characters,but its kerning is wrong! Anybody can guide me?

Ando Masahashi
  • 3,112
  • 2
  • 24
  • 41
user3513329
  • 121
  • 3
  • try Paint.measureText, call it two times: first with start == 0 and end == i+1 and second time with start == i and end == i+1 and substract two results – pskink Apr 25 '14 at 07:32
  • i forgot to mention to use three params measureText – pskink Apr 25 '14 at 07:41

2 Answers2

0

Auto kerning solution based on suggestion in the comment of pskink Why the kerning is wrong when I use canvas.drawText? But also supports overriding the kerning with a fixed value (as in e.g. SVG)

if (isKerningValueSet) {
    glyphPosition += kerningValue;
} else {
    float previousChar = paint.measureText(line, Math.max(0, index - 1), index);
    float previousAndCurrentChar = paint.measureText(line, Math.max(0, index - 1), index + 1);
    float onlyCurrentChar = paint.measureText(line, index, index + 1);
    float kernedCharWidth = previousAndCurrentChar - previousChar;
    float kerning = kernedCharWidth - onlyCurrentChar;
    glyphPosition += kerning;
}
msand
  • 468
  • 4
  • 8
  • skia seems to support getKerningPairAdjustments, but I haven't found it exposed by android graphics anywhere in the java api. https://github.com/google/skia/blob/master/include/core/SkTypeface.h#L239-L260 https://github.com/android/platform_external_skia/blob/master/src/core/SkTypeface.cpp#L261-L273 – msand Jun 21 '17 at 16:18
-1

I use this for kerning. This is written in coffeescript btw, easy to convert it though.

_fillText = Canvas.Context2d::fillText
Canvas.Context2d::fillText = (str, x, y, args...) ->

  # no kerning? default behavior
  return _fillText.apply this, arguments unless @kerning?

  # we need to remember some stuff as we loop
  offset = 0

  _.each str, (letter) =>

    _fillText.apply this, [
      letter
      x + offset + @kerning
      y
    ].concat args # in case any additional args get sent to fillText at any time

    offset += @measureText(letter).width + @kerning

then to use it

context.kerning = 20
context.fillText(....)

Here's the javascript (without the args... part, but I don't think anything additional is sent to fillText by default)

var _fillText;

_fillText = Canvas.Context2d.prototype.fillText;

Canvas.Context2d.prototype.fillText = function(str, x, y) {
  var offset;
  // Fallback unless we need kerning
  if (this.kerning == null) {
    return _fillText.apply(this, arguments);
  }
  offset = 0;
  return _.each(str, (function(_this) {
    return function(letter) {
      _fillText.call(_this, letter, x + offset + _this.kerning, y);
      return offset += _this.measureText(letter).width + _this.kerning;
    };
  })(this));
};
dansch
  • 6,059
  • 4
  • 43
  • 59