17
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Lambdaの実行環境にフォントを追加する

はじめに

AWS LambdaでPhantomJS日本語フォント対応では fontconfig をビルドしてデプロイパッケージに含めているが、 Lambdaの実行環境を確認したところ、fontconfig は導入されている。
したがって、フォントキャッシュさえ生成すれば、実行環境のfontconfigが利用できる。

なおLambdaの実行環境のfontconfigはXDGには対応していない。
検証時、fc-cache のバージョンは 2.8.0 であった。

Lambda の実行時 LAMBDA_TASK_ROOT=/var/task/share/fonts となっている。

fontconfig は

~/.fonts.conf
~/.fonts
~/.fontconfig

は見るので、HOME=$LAMBDA_TASK_ROOT に設定して、これらが

$HOME/
        .fontconfig
        .fonts
        .fonts.conf

というツリー構造で見えるようにデプロイパッケージに含めてやれば良い。

フォントキャッシュの生成

NotoSansCJK のパッケージから NotoSansCJK-Regular.ttc を抽出して、.fonts に配置する。

Lambda の実行時に $LAMBDA_TASK_ROOT/.fontconfig は書き込みできないため、そのままでは fc-cache の実行でキャッシュファイルの作成に失敗する。

いったん /tmp/cache/fontconfig にキャッシュを生成して、デプロイパッケージでは .fontconfig に配置する。

このため、次の内容で .fonts.conf を作成する。

fonts.conf
<fontconfig>
    <cachedir>/tmp/cache/fontconfig</cachedir>
</fontconfig>

フォントキャッシュを生成する Lambda 関数は次の通り。

fontcache.py
from __future__ import print_function
import subprocess

import sys
import os
import base64
import boto3
import logging
from os.path import join

logger = logging.getLogger()  
logger.setLevel(logging.INFO)  

BUCKET_NAME = 'バケット名'

def lambda_handler(event, context):
    os.environ['HOME'] = os.environ['LAMBDA_TASK_ROOT']
    try:
        os.makedirs('/tmp/cache/fontconfig')
    except OSError as e:
        (errno, strerror) = e
        print('OSError {}'.format(strerror))

    args = [ 'fc-cache', '-v', join(os.environ['HOME'], '.fonts') ]
    p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    returncode = p.returncode
    stdout_data, stderr_data = p.communicate()

    print('stdout_data:\n' + stdout_data.decode('utf-8'))
    print('stderr_data:\n' + stderr_data.decode('utf-8'))
    s3bucket = boto3.resource('s3').Bucket(BUCKET_NAME)
    tmp_fontconfig = '/tmp/cache/fontconfig'
    for cache in os.listdir(tmp_fontconfig):
        response = s3bucket.upload_file(join(tmp_fontconfig, cache), cache)
        with open(join(tmp_fontconfig, cache), mode='rb') as f:
            print("{}:\n{}\n".format(cache, base64.b64encode(f.read())))

デプロイパッケージの内容

% unzip -l fontcache.zip 
Archive:  fontcache.zip
  Length     Date   Time    Name
 --------    ----   ----    ----
     1116  10-16-17 15:34   fontcache.py
      105  10-12-17 20:54   .fonts.conf
        0  10-12-17 22:01   .fonts/
 18748872  10-12-17 17:04   .fonts/NotoSansCJK-Regular.ttc
 --------                   -------
 18750093                   4 files

この Lambda 関数を実行すると、S3 にキャッシュファイルがアップロードされる。

テスト

先ほど生成したキャッシュファイルを .fontconfig に配置する。

phantomjs のロードモジュールと rasterize.jsbin ディレクトリに配置する。

phantomjs でのスクリーンキャプチャーは、Screen Capture | PhantomJSを参考に、 rasterize.js 使用してテストプログラムを作成した。

phantomjs.py
from __future__ import print_function
import subprocess
import tempfile
import os
import boto3

def phantomjs(url, bucket, name):
    from os.path import join
    run_dir = os.environ['LAMBDA_TASK_ROOT']
    bin_dir = join(run_dir, 'bin')
    args = [ join(bin_dir, 'phantomjs'), join(bin_dir, 'rasterize.js'), url ]

    with tempfile.NamedTemporaryFile(suffix='.png') as f:
        args.append(f.name)
        print('{}\n'.format(' '.join(args)))
        p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        returncode = p.returncode
        stdout_data, stderr_data = p.communicate()

        s3 = boto3.resource('s3')
        bucket = s3.Bucket(bucket)
        response = bucket.upload_file(f.name, name)

    print('returncode {}\n'.format(returncode))
    print('stdout:: \n{}\n'.format(stdout_data.decode('utf-8')))
    print('stderr:: \n{}\n'.format(stderr_data.decode('utf-8')))
    print('stderr:: \n{}\n'.format(stderr_data.decode('utf-8')))
    print('response {}\n'.format(response))

def lambda_handler(event, context):
    os.environ['HOME'] = os.environ['LAMBDA_TASK_ROOT']
    os.chdir(os.environ['LAMBDA_TASK_ROOT'])
    phantomjs('https://www.amazon.co.jp', 'バケット名', 'screenshot.png')

デプロイパッケージの内容

% unzip -l phantomjs.zip 
Archive:  phantomjs.zip
  Length     Date   Time    Name
 --------    ----   ----    ----
     1304  10-13-17 17:09   phantomjs.py
      105  10-12-17 20:54   .fonts.conf
        0  10-12-17 22:01   .fonts/
 18748872  10-12-17 17:04   .fonts/NotoSansCJK-Regular.ttc
        0  10-12-17 23:05   .fontconfig/
    12792  10-12-17 22:27   .fontconfig/996789b4ba9d471a5fd80c008c2b2acf-le64.cache-3
 67932064  01-25-16 10:01   bin/phantomjs
     2241  10-12-17 23:19   bin/rasterize.js
 --------                   -------
 86697378                   8 files

この Lambda 関数を実行して、うまく画面がキャプチャーされていればよい。

phantomjs の実行のために、Lambda が使用するメモリを192MB以上にしておかないと、メモリ不足で処理が途中で打ち切られる。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
17
Help us understand the problem. What are the problem?