wiki:BadEPSHeader

Python Imaging Library に "bad EPS Header" と怒られる

2009/01/06 ymasuda

PS (またはEPS) ファイルを Python Imaging Library で扱っていて、 "bad EPS Header" というメッセージの IOError 例外に遭遇したことはないでしょうか?

問題を起こす PS ファイルのヘッダを見ると、だいたいこんな感じ。

%!PS-Adobe-3.0
%%Pages: (atend)
%%BoundingBox: 0 0 36 22
%%HiResBoundingBox: 0.000000 0.000000 36.000000 22.000000
%.............................................
%%Creator: GPL Ghostscript 857 (pswrite)
%%CreationDate: 2009/01/06 11:56:40
%%DocumentData: Clean7Bit
%%LanguageLevel: 2
%%EndComments

%!PS-Adobe-3.0 の次の行から、 %%EndComments までがヘッダブロックで、 間に入っている % で始まる行がヘッダの内容です。 %%BoundingBox: 0 0 36 22 のように、 % 二個で開始して、 <ワードキャピタルのキーワード>: <値><行末> の形式をとっているのが ヘッダのメタデータ行です。

さて、PIL では、 EPS ファイルの処理は EPSImagePlugin というモジュールが担当しています。 EPSImagePlugin は、 PostScript? ヘッダを解析するときに、ヘッダブロックのメタデータ行が連続していなければ、ヘッダが 壊れているとみなします。ところが、上のファイルでは、5行目が %...................... で、 正しい(?)メタデータ行の形式ではありません。このため、 EPSImagePlugin はこのヘッダを壊れた ヘッダであるとみなすわけです。

PostScript? ヘッダの厳密な仕様ではどうなっているんだろう?結構多くのツールがこの「壊れた」形式のヘッダを 出力するようです。そこで、私は、 PIL で処理するときには、以下のようなフィルタを書いて、ヘッダから「不要な」 メタデータを取り除いています。

#!/usr/bin/env python

import sys
import re

ORDINAL_FIELD = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
ENDCOMMENT_RE=re.compile(r'%%EndComments\s*$')

def filter(infilename, outfilename):
  infile = open(infilename, 'rb')
  outfile = open(outfilename, 'wb')
  line = infile.readline()
  if not line.startswith('%!PS-Adobe'):
    raise IOError('Not valid ps file.')
  outfile.write(line)
  while(1):
    line = infile.readline()
    if line=='':
      raise IOError('Reached EOF before EndComments.')
    if ORDINAL_FIELD.match(line):
      outfile.write(line)
    else:
      if ENDCOMMENT_RE.match(line):
        outfile.write(line)
        break
  # write remaining part
  outfile.write(infile.read())
  infile.close()
  outfile.close()

if __name__=="__main__":
  from os.path import split
  if len(sys.argv)!=3:
    scriptname = split(__file__)[-1]
    sys.stderr.write('Usage: %s <infilename> <outfilename>\n' %(scriptname))
    sys.exit(1)
  try:
    filter(sys.argv[1], sys.argv[2])
  except Exception, e:
    sys.stderr.write(str(e)+'\n')
    sys.exit(1)
  sys.exit(0)