Google Tag Manager

2024/10/31

Move and rotate strings to place them along the Shape curve

Code

public static Shape createTextOnPath(Shape shape, GlyphVector gv) {
  double[] points = new double[6];
  Point2D prevPt = new Point2D.Double();
  double nextAdvance = 0d;
  double next = 0d;
  Path2D result = new Path2D.Double();
  int length = gv.getNumGlyphs();
  int idx = 0;
  PathIterator pi = new FlatteningPathIterator(
      shape.getPathIterator(null), 1d);
  while (idx < length && !pi.isDone()) {
    switch (pi.currentSegment(points)) {
      case PathIterator.SEG_MOVETO:
        result.moveTo(points[0], points[1]);
        prevPt.setLocation(points[0], points[1]);
        nextAdvance = gv.getGlyphMetrics(idx).getAdvance() * .5;
        next = nextAdvance;
        break;

      case PathIterator.SEG_LINETO:
        double dx = points[0] - prevPt.getX();
        double dy = points[1] - prevPt.getY();
        double distance = Math.hypot(dx, dy);
        if (distance >= next) {
          double r = 1d / distance;
          double angle = Math.atan2(dy, dx);
          while (idx < length && distance >= next) {
            double x = prevPt.getX() + next * dx * r;
            double y = prevPt.getY() + next * dy * r;
            double advance = nextAdvance;
            nextAdvance = getNextAdvance(gv, idx, length);
            AffineTransform at = AffineTransform.getTranslateInstance(x, y);
            at.rotate(angle);
            Point2D pt = gv.getGlyphPosition(idx);
            at.translate(-pt.getX() - advance, -pt.getY());
            Shape s = gv.getGlyphOutline(idx);
            result.append(at.createTransformedShape(s), false);
            next += advance + nextAdvance;
            idx++;
          }
        }
        next -= distance;
        prevPt.setLocation(points[0], points[1]);
        break;

      default:
    }
    pi.next();
  }
  return result;
}

References