💎

Sketchファイルから動的にOGP画像を生成する

2023/02/12に公開

どうもこんにちは!みなさんブログのOGP画像ってどうしてますか?

OGP画像を生成する方法としては、Vercelが出してるsatori@vercel/ogのようなHTMLから画像を動的に生成するやり方というのが最近注目されていますが、この記事では別の方法、即ちタイトルに書いてある通りSketchファイルからOGP画像を生成する方法を紹介したいと思います。

概要としては、テンプレートとなるSketchファイルをNode.jsで書いたプログラムで動的に変更して、sketchtoolというSketchに付属しているコマンドラインツールを使って変更を加えたSketchファイルから実際の画像を書き出すという流れになります。

詳細

まずこういう感じのSketchファイルを作ってローカルに保存します。このSketchファイルはOGP画像を生成するためのテンプレートの役割を果たします。

書き出したい画像のアートボードの名前をogp-image、タイトルとしてあとで変更したいレイヤーには$titleという名前をつけていることに注目してください。

レイヤーの名前を元にプログラムでSketchファイルを動的に変更を加えるTypeScript+Node.jsのコードはこういう感じになります。コード中でSketchファイルを編集するのに使っている@sketch-hq/sketch-file@sketch-hq/sketch-file-format-tsは、ありがたいことにSketchの開発元が公式に提供しているnpmパッケージです。

import { fromFile, SketchFile, toFile } from '@sketch-hq/sketch-file';
import FileFormat from '@sketch-hq/sketch-file-format-ts/dist/esm';
import { $ } from 'zx';

generateOGPImage({
  title: "WasmでJavaScriptを動かす意義", // ← $titleレイヤに実際に表示したい文字列を指定
  outputDir: "./output"
});

async function generateOGPImage({title, outputDir}: {title: string; outputDir: string;}) {
  // 1. テンプレートとして作ったSketchファイルを読み込み
  const file = await fromFile('./ogp-image.sketch');
  
  // 2. レイヤの中身を動的に変更する($titleという名前のレイヤーに実際の記事タイトルを埋める)
  const exportItemId = fill({
    file,
    title,
  });

  // 3. 動的に変更したSketchファイルを別のパスに保存する
  const exportableFile: SketchFile = {
    contents: file.contents,
    filepath: "./ogp-image-output.sketch",
  };  
  await toFile(exportableFile);

  // 4. sketchtoolのCLIを叩いて実際の画像をexportする
  const sketchtool = '/Applications/Sketch.app/Contents/MacOS/sketchtool';
  await $`${sketchtool} export layers ogp-image-output.sketch --item=${exportItemId} --output="${outputDir}"`;
}

function fill({file, title}: {file: SketchFile; title: string;}): string {
  let targetItemId: string | null = null;
  const parseLayer = (layer: FileFormat.AnyLayer) => {
    switch (layer._class) {
      case 'artboard':
        
        if (layer.name === 'ogp-image') {
	  // ogp-imageという名前のアートボードがあったらexport時に必要となるレイヤのIDを控えておく
          targetItemId = layer.do_objectID;
        }
      case 'group':
      case 'page':
      case 'shapeGroup':
        // ツリー上になっているレイヤを辿っていく
        for (const child of layer.layers) {
          parseLayer(child);
        }
        return;
      case 'text':
        // $titleという名前のレイヤーがあったら実際の記事タイトルに変換する
        if (layer.name === '$title') {
          layer.attributedString.string = title;
          layer.attributedString.attributes[0].length = title.length;
        }
    }
  };

  for (const page of file.contents.document.pages) {
    parseLayer(page);
  }

  if (typeof targetItemId !== 'string') {
    throw Error();
  }

  // exportするために必要なレイヤのIDを返す
  return targetItemId;
}

コード中で使っているパッケージはnpm installなどで適宜インストールしてください。このコードを実行すると、Sketchファイルの中身を動的に書き換えた上で下のような画像がexportされます。タイトルの部分が動的に書き換わっていることがわかると思います。

この要領で、ブログ記事ごとに処理していくと簡単に個別のOGP画像を生成することができます。Sketchで直接デザインを作れるので直感的に画像を生成できて大変便利です。SVGをプログラムで書き出す場合とは違って、テキストの中央寄せや折り返しなども非常に簡単です。

@vercel/ogのようにサーバーサイドでOGP画像を生成するのとは違って、この方法ではSketchアプリをインストールしているマシンでしか画像を生成できませんが、個人のブログレベルの記事数の場合であればこの方法でも十分ではないでしょうか。

以上Sketchファイルから動的にOGP画像を生成する方法でした。

Discussion