MatplotlibとShapelyとgdstkを使用して日本語をポリゴン化&GDSII化

# はじめに

MatplotlibとShapelyを使用したテキストのポリゴン化と、gdstk を用いたGDSIIファイル形式への書き出しについて紹介します。

https://colab.research.google.com/drive/1J5d_zpd1JNRzPnaLaCEcsjFv56bvyAqY?usp=sharing

に実行結果は載せています。

以下にその方法を説明します。

## 必要なライブラリのインポート

まず、必要なライブラリをインポートします。

```python:
from matplotlib.textpath import TextPath
from shapely.geometry import Polygon
from matplotlib import font_manager as fm
import matplotlib.pyplot as plt
```

- `TextPath` は`Matplotlib`でテキストからパスを生成するために使用します。
- `font_manager` はフォントのプロパティを管理します。
- `Polygon` は`Shapely`でポリゴンを扱うためのクラスです。


## フォントプロパティの設定

テキストをポリゴン化するためには、フォントの設定が必要です。ここでは、日本語フォントのパスを指定します。

``` :
fp = fm.FontProperties(fname='./NotoSerifJP-Regular.otf')
```

日本語のフォントは、

https://fonts.google.com/?subset=japanese&noto.script=Hira

から好きなフォントのファイルを取得してます。

`FontProperties` にはフォントファイルへのパスを指定します。例では、`Noto Serif JP`フォントを使用しています。

[google colab](https://colab.research.google.com/drive/1J5d_zpd1JNRzPnaLaCEcsjFv56bvyAqY?usp=sharing) の方では、

``` python:
import gdown # https://github.com/wkentaro/gdown
# download data
fileURL="https://drive.google.com/file/d/1ZSovgMRADTRnLAk3K5YlnjcJx1HVeTtF/view?usp=sharing" # download NotoSerifJP-Regular.otf from my drive
localfile="NotoSerifJP-Regular.otf"
gdown.download(fileURL, localfile, quiet=False, fuzzy=True)
```

のように、私のgoogle drive から、gdown で取得して使うような設定にしてます。


## テキストからポリゴンを生成する関数

テキストを受け取り、それをポリゴンに変換する関数を定義します。

``` python:
def text_to_polygons(text, fontsize, fontprop):
    # テキストからTextPathを生成
    tp = TextPath((0, 0), text, size=fontsize, prop=fontprop)

    # TextPathからポリゴンを生成
    polygons = []
    for path in tp.to_polygons(closed_only=False):
        if len(path) == 0:
            continue
        exterior = path
        polygon = Polygon(exterior)
        polygons.append(polygon)
    return polygons
```

この関数は、テキストから TextPath を生成し、そのパスを Polygon オブジェクトに変換します。


# コード全文

## ジオメトリの生成と確認

``` python:
from matplotlib.textpath import TextPath
from shapely.geometry import Polygon
from matplotlib import font_manager as fm
import matplotlib.pyplot as plt

# フォントプロパティの設定
fp = fm.FontProperties(fname='./NotoSerifJP-Regular.otf')  # 日本語フォントへのパスを指定

def text_to_polygons(text, fontsize, fontprop):
    """
    テキストから外周と内部の穴を持つポリゴンを生成する。

    Parameters:
    text (str): テキスト。
    fontsize (int): フォントサイズ。
    fontprop (FontProperties): フォントプロパティ。

    Returns:
    list: Polygonオブジェクトのリスト。
    """
    tp = TextPath((0, 0), text, size=fontsize, prop=fontprop)
    polygons = []

    for path in tp.to_polygons(closed_only=False):
        if len(path) == 0:
            continue
        exterior = path  # 外周
        polygon = Polygon(exterior)  # 内部の穴はないと仮定
        polygons.append(polygon)

    return polygons

# 使用例
text_polygons = text_to_polygons("2023年12月18日 製作", 100, fp)

# プロット
fig, ax = plt.subplots()
for polygon in text_polygons:
    x, y = polygon.exterior.xy
    ax.plot(x, y, color="black")

    for interior in polygon.interiors:
        x, y = interior.xy
        ax.plot(x, y, color="red")

ax.set_aspect('equal', adjustable='box')
plt.show()
```

## 実行結果

![スクリーンショット 2023-12-18 11.01.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179652/1f33e0e2-5e21-e010-51ec-ea10bf271854.png)


## GDSIIファイルへの書き出し

``` python:
# LSI回路のジオメトリを作成
lib = gdstk.Library()
cell = lib.new_cell("IMAGER_ARRAY")

tag = {
    "pixel": {"layer": 1, "datatype": 1},
    "int": {"layer": 5, "datatype": 5},
    "ext": {"layer": 10, "datatype": 10},
}

# Convert shapely geometries to gdstk polygons
for polygon in text_polygons:
    coords = polygon.exterior.coords
    cell.add(gdstk.Polygon(coords, **tag["ext"]))
    for interior in polygon.interiors:
        print ("interior")
        coords = interior.coords
        cell.add(gdstk.Polygon(coords, **tag["int"]))

# Save the GDS file
outgdsfile="japanese.gds"
lib.write_gds(outgdsfile)

# GDSファイルに保存
lib.write_gds(outgdsfile)
plot_gds_onlypoly(outgdsfile, debug=False)
```

## 実行例

![スクリーンショット 2023-12-18 11.01.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179652/26efb9a4-6a64-e5d7-4ff4-25244329c5fb.png)