Curved Text

by Steve Eberhardt

HTML

<script src="https://rawgit.com/fabricjs/fabric.js/master/dist/fabric.js"></script>


<canvas id="c" width="600" height="400"></canvas>

CSS

canvas {
    border: 1px solid #999;
}

JavaScript

var canvas = new fabric.Canvas('c');

var path = new fabric.Path('M 0 0 A 20 20 0 0 1 300 0', {
  	strokeWidth: 2,
  	stroke: 'red',
    left: 100,
    top: 150,
    fill: 'transparent'
  });

var text = new fabric.Text('One Ring to rule them', {
  path: path,
  fontSize: 44,
  left: 100,
  top: 150,
  startOffset: 0.1
});
canvas.add(path);
canvas.add(text);


fabric.Text.prototype._measureLine = function(lineIndex) {
      var width = 0, i, grapheme, line = this._textLines[lineIndex], prevGrapheme,
          graphemeInfo, numOfSpaces = 0, lineBounds = new Array(line.length),
          positionInPath = 0, startingPoint, totalPathLength, path = this.path;

      this.__charBounds[lineIndex] = lineBounds;
      if (path) {
        startingPoint = fabric.util.getPointOnPath(path.path, 0, path.segmentsInfo);
        
        totalPathLength = path.segmentsInfo[path.segmentsInfo.length - 1].length;
        startingPoint.x += path.pathOffset.x;
        startingPoint.y += path.pathOffset.y;
        positionInPath = this.startOffset ? this.startOffset * totalPathLength : 0;
      }
      for (i = 0; i < line.length; i++) {
        grapheme = line[i];
        graphemeInfo = this._getGraphemeBox(grapheme, lineIndex, i, prevGrapheme);
        if (path) {
          if (positionInPath > totalPathLength) {
            positionInPath %= totalPathLength;
          }
         
          // it would probably much fater to send all the grapheme position for a line
          // and calculate path position/angle at once.
          this._setGraphemeOnPath(positionInPath, graphemeInfo, startingPoint);
        }
        lineBounds[i] = graphemeInfo;
        width += graphemeInfo.kernedWidth;
        positionInPath += graphemeInfo.kernedWidth;
        prevGrapheme = grapheme;
      }
      // this latest bound box represent the last character of the line
      // to simplify cursor handling in interactive mode.
      lineBounds[i] = {
        left: graphemeInfo ? graphemeInfo.left +...