Jetbrains Projectorのデフォルトフォントだと日本語表示が微妙なので別のフォントを埋め込む


追記

  • 2021/05/20: projector-client commit:8e89968 で日本語入力に対応したようです。

まだリリース前なので、試す場合のために別の記事を用意しました。

  • 2021/05/02: projector-server 1.1.4でデフォルトフォントがNotoになりました。(CHANGELOG.md - 1.1.4)

はじめに

JetbrainsのBlogを眺めていたら新製品としてProjectorが紹介されていました。

The JetBrains Blog: Access Your IDE Remotely With Projector

ProjectorはJetbrainsのIDEをリモートで利用するための製品です。

自分のPCにインストールしてあるIDEをリモートから使うものではありません。

サーバーを建てて、専用クライアントまたはブラウザからアクセスして利用します。

DockerでProjector(WebStorm)を起動し、FireFoxでアクセスしたときの画面は・・・

そうだね、いつものJetbrains IDEだね!

動きについては、普通に使うのと変わりなくてよくわからない。

通信についてはRDPやX11 Forwarding、VNCと比べてほとんど無く、画面の描画に必要な部分を貰ってきて描画している様子。

強いサーバ上でうまく開発者毎に環境を提供できるようにすれば、良いリモート開発環境を作ることができそう。

ただ、デフォルトのフォントで表示する日本語が何とも言えない感じ。

この記事では、そんな何とも言えない日本語表示をいい感じにするため、JetBrains/projector-serverに手を加え、デフォルト以外のフォント(例としてCica)を埋め込む方法を示します。

注意点

  • この記事を書いた時点(2021年3月23日)では日本語入力には対応していません
  • "ubuntu font family"と"JetBrains Mono"が組み込まれているので、日本語フォントを埋め込まずとも日本語は表示されます
  • IDE以外は表示できない(サーバーにインストールされたFireFoxとか

手順

1. JetBrains/projector-serverをcloneする

2. projector-server/build.gradletask downloadMonoFontsの下あたりに以下のtaskを追加する(ただのdownloadMonoFontsのコピー

task downloadCicaFonts {
  def cicaFontsLink = "https://github.com/miiton/Cica/releases/download/v5.0.2/Cica_v5.0.2_with_emoji.zip"

  doLast {
    def requiredFonts = ["Cica-R", "Cica-RI", "Cica-B", "Cica-BI"].stream().map { "$fontsPath/${it}.ttf" }.collect()

    println("Checking cica fonts: $requiredFonts")

    def haveAll = requiredFonts.stream().allMatch { project.file(it).exists() }

    if (haveAll) {
      println("Cica fonts already exist, skipping download.")
    }
    else {
      println("Some cica fonts are missing, downloading... If some fonts exist, they will be overwritten.")

      project.file(fontsPath).mkdirs()

      def url = new URL(cicaFontsLink)
      def tempFile = File.createTempFile("cicaFonts", "zip")
      url.withInputStream { i -> tempFile.withOutputStream { it << i } }

      def originalToDest = [
        "Cica-Regular"      : "Cica-R",
        "Cica-RegularItalic": "Cica-RI",
        "Cica-Bold"         : "Cica-B",
        "Cica-BoldItalic"   : "Cica-BI",
      ]

      def zipFile = new ZipFile(tempFile)

      originalToDest.forEach { original, dest ->
        def destFile = project.file("$fontsPath/${dest}.ttf")
        def srcPath = "${original}.ttf"

        destFile.delete()
        destFile.createNewFile()

        destFile.withOutputStream { it << zipFile.getInputStream(zipFile.getEntry(srcPath)) }
      }

      tempFile.delete()

      println("Download complete")
    }
  }
}

3. 同じファイルのtask downloadFontsを修正する

task downloadFonts {
-  dependsOn(downloadDefaultFonts, downloadMonoFonts)
+  dependsOn(downloadDefaultFonts, downloadMonoFonts, downloadCicaFonts)
}

4. projector-server/src/main/kotlin/org/jetbrains/projector/server/service/ProjectorFontProvider.ktの下の方にあるMONO_BI_NAMEMONO_BI_PATHの下あたりに以下のコードを追加する

  private const val CICA_R_NAME = "Cica-R"
  private const val CICA_R_PATH = "/fonts/Cica-R.ttf"

  private const val CICA_RI_NAME = "Cica-RI"
  private const val CICA_RI_PATH = "/fonts/Cica-RI.ttf"

  private const val CICA_B_NAME = "Cica-B"
  private const val CICA_B_PATH = "/fonts/Cica-B.ttf"

  private const val CICA_BI_NAME = "Cica-BI"
  private const val CICA_BI_PATH = "/fonts/Cica-BI.ttf"

5. 同じファイルの上の方にあるprivate val monoBoldItalic by lazy { loadFont(MONO_BI_NAME, MONO_BI_PATH) }の下あたりに以下のコードを追加する

  private val cicaRegular by lazy { loadFont(CICA_R_NAME, CICA_R_PATH) }
  private val cicaRegularItalic by lazy { loadFont(CICA_RI_NAME, CICA_RI_PATH) }
  private val cicaBold by lazy { loadFont(CICA_B_NAME, CICA_B_PATH) }
  private val cicaBoldItalic by lazy { loadFont(CICA_BI_NAME, CICA_BI_PATH) }

6. 同じファイルのprivate val allInstalledFonts by laze {...}内のlistOfを修正する

  private val allInstalledFonts by lazy {
    fun Font2D.toFont() = Font(getFamilyName(null), style, DEFAULT_SIZE)

    listOf(
      defaultRegular,
      defaultRegularItalic,
      defaultBold,
      defaultBoldItalic,
      monoRegular,
      monoRegularItalic,
      monoBold,
-     monoBoldItalic
+     monoBoldItalic,
+     cicaRegular,
+     cicaRegularItalic,
+     cicaBold,
+     cicaBoldItalic
    ).map(Font2D::toFont)
  }

7. 同じファイルのprivate fun isMonospacedFont(name: String): Booleanの下あたりに以下のコードを追加する

  private fun isCicaFont(name: String): Boolean {
    return "cica" in name.toLowerCase()
  }

8. 同じファイルのoverride fun findFont2D(name: String, style: Int, fallback: Int): Font2Dを修正する

  override fun findFont2D(name: String, style: Int, fallback: Int): Font2D {
    if (isMonospacedFont(name)) {
      return when (style) {
        Font.BOLD or Font.ITALIC -> monoBoldItalic

        Font.BOLD -> monoBold

        Font.ITALIC -> monoRegularItalic

        else -> monoRegular
      }
    }
+   else if (isCicaFont(name)) {
+     return when (style) {
+       Font.BOLD or Font.ITALIC -> cicaBoldItalic
+
+       Font.BOLD -> cicaBold
+
+       Font.ITALIC -> cicaRegularItalic
+
+       else -> cicaRegular
+     }
    }
    else {
      return when (style) {
        Font.BOLD or Font.ITALIC -> defaultBoldItalic

        Font.BOLD -> defaultBold

        Font.ITALIC -> defaultRegularItalic

        else -> defaultRegular
      }
    }
  }

動作確認

この動作確認ではDockerを利用します。

1. JetBrains/projector-docker修正済みのproject-serverがあるディレクトリにcloneする

ディレクトリ階層イメージ

/
|- projector-server/ <- 最初にcloneしたprojector-server
|- projector-docker/  <- 動作確認用にcloneしたprojector-docker

2. projector-dockerに移動する

3. ./build-container.shを実行する

例) WebStorm 2020.3.2

./build-container.sh projector-webstorm:2020.3.2 https://download.jetbrains.com/webstorm/WebStorm-2020.3.2.tar.gz

4. ./build-container.sh上の階層にあるproject-server(つまり修正済みのproject-server)を参照してビルドとかしてくれるので気長に待つ

5. 起動させる

docker run -it --rm -p 8887:8887 projector-webstorm:2020.3.2

6. ブラウザからアクセスする

http://{起動させたホストIP}:8887/

7. フォントを設定してみる

Show only monospaced fontsのチェックを外すのを忘れないように

やったぜ

最後に

応用すれば他のフォントも埋め込めます。

FiraCodeを試したところ、リガチャも普通に表示されました。

日本語フォントを導入すると、ブラウザアクセス後すぐの画面(下の画像の画面)の時間が少し長くなります。

それは置いといて、このProjectorのすごいところはPluginもだいたい動くところです。

例えば公式の日本語化プラグインも動きます。

フォントを埋め込む方法に至るまでにいろいろ試しました。

  • 敗北: /usr/local/share/fontsにフォントを設置してみる
  • 敗北: ~/.local/share/fontsにフォントを設置してみる
  • 敗北: /projector/ide/jbr/lib/fonts/にフォントを設置してみる
  • などなど・・・

単純にIDEがフォントを認識している様子はありませんでしたので、最終的にprojector-serverの方に手を加えて目的を達成しました。

あとは、projector-dockerのDockerfile自体を修正したりして自分の考える最強の開発環境を作り上げればいいと思います。