---
category: swing
folder: TextOnPath
title: Shape上に文字列を配置する
tags: [Font, GlyphVector, Shape, Path2D, FlatteningPathIterator]
author: aterai
pubdate: 2024-09-23T07:31:47+09:00
description: Path2Dなどの曲線上に添うよう文字列を移動・回転して配置します。
image: https://drive.google.com/uc?id=1kBK9anV1EnAjXes9rjN3o57Cyk_Qinnw
hreflang:
    href: https://java-swing-tips.blogspot.com/2024/10/move-and-rotate-strings-to-place-them.html
    lang: en
---
* 概要 [#summary]
`Path2D`などの曲線上に添うよう文字列を移動・回転して配置します。

#download(https://drive.google.com/uc?id=1kBK9anV1EnAjXes9rjN3o57Cyk_Qinnw)

* サンプルコード [#sourcecode]
#code(link){{
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;
}
}}

* 解説 [#explanation]
- `Arc2D`で作成した曲線を[https://docs.oracle.com/javase/jp/8/docs/api//java/awt/geom/FlatteningPathIterator.html FlatteningPathIterator]で平坦化(パスを直線に変更)した`PathIterator`に変換
-- [[FlatteningPathIteratorでShape上の点を取得する>Swing/FlatteningPathIterator]]
- 文字列を`Font#createGlyphVector(...)`で`GlyphVector`に変換
- `GlyphVector#getGlyphOutline(...)`で文字のアウトライン図形を取得
- `Math.hypot(dx, dy)`で移動距離、`Math.atan2(dy, dx)`で回転角度を取得して`AffineTransform`で文字のアウトライン図形を変換
-- [http://www.jhlabs.com/java/java2d/strokes/ Fun with Java2D - Strokes]の[http://www.jhlabs.com/java/java2d/strokes/ShapeStroke.java ShapeStroke.java]から繰り返しなし、文字間拡張なしを参考
- `Path2D#append(...)`で変換した文字アウトラインを追加して、`Shape`上に文字列を配置した`Path2D`を生成

* 参考リンク [#reference]
- [http://www.jhlabs.com/java/java2d/strokes/ Fun with Java2D - Strokes]
- [[FlatteningPathIteratorでShape上の点を取得する>Swing/FlatteningPathIterator]]

* コメント [#comment]
#comment
#comment