#!/bin/bash
#
# cmap-info - show CMap information
#
# Copyright (C) 2010 Taiji Yamada <taiji@aihara.co.jp>
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
i=0
while [ "$1" != "" ]; do
  case "$1" in
  --detail)
    OPTIONS="$OPTIONS -ddetail"
    ;;
  --no-filename)
    OPTIONS="$OPTIONS -dno-filename"
    ;;
  --no-cidsysteminfo)
    OPTIONS="$OPTIONS -dno-cidsysteminfo"
    ;;
  --newest-only)
    OPTIONS="$OPTIONS -dnewest-only"
    ;;
  --obsolete-only)
    OPTIONS="$OPTIONS -dobsolete-only"
    ;;
  --obsolete-info)
    OPTIONS="$OPTIONS -dobsolete-info"
    ;;
  *Adobe-Korea1-H-Mac)	# illegal CMap resource
    ;;
  *)
    case "$(file "$1")" in
    *": PostScript document"*)
      files[$i]="$1"
      i=$(($i+1))
    esac
    ;;
  esac
  shift
done

{ cat <<'EOF' && for f in "${files[@]}"; do echo $f; done; } | gs -dSAFER -q -dNOPAUSE -dBATCH -dNODISPLAY $OPTIONS -
%!
/unknowndef { exch dup where { pop pop pop } { exch def } ifelse } bind def

/detail false unknowndef
/no-filename false unknowndef
/no-cidsysteminfo false unknowndef
/newest-only false unknowndef
/obsolete-only false unknowndef
/obsolete-info false unknowndef
/register-mode newest-only obsolete-only obsolete-info or or def

/isprint { % integer isprint boolean
  false [ (\t) (\n) (\r) ] {			% d bool chr
    0 get 2 index eq { pop true exit } if	% d bool chr=int {pop true} if
  } forall dup not { 1 index 16#20 ge 2 index 16#7F lt and or } if exch pop
} bind def

/concat2strings { % str1 str2 concat2strings str
  2 { dup type /stringtype ne { dup length string cvs } if exch } repeat
  exch dup length 3 2 roll dup length 2 index add string 0 5 1 roll
  2 { dup 4 2 roll putinterval } repeat
} bind def

/concats { % [str ...] concats string
  () exch dup length 1 sub -1 0 {
    1 index exch get 3 -1 roll concat2strings exch
  } for pop
} bind def

/concatanytostringprocs <<
  /dicttype	{
    exch (<< ) concat2strings exch
    {
      1 dict begin
	/value exch def
	concatanytostring value concatanytostring
      end
    } forall
    (>> ) concat2strings
  } bind
  /arraytype	{
    dup xcheck {
      exch ({ ) concat2strings exch
      { concatanytostring } forall
      (} ) concat2strings
    } {
      exch ([ ) concat2strings exch
      { concatanytostring } forall
      (] ) concat2strings
    } ifelse
  } bind
  /stringtype	{
    exch (\() concat2strings exch
    {
      dup isprint {
	1 string dup 0 4 -1 roll put concat2strings
      } {
	exch (\\) concat2strings exch
	8 3 string cvrs
	dup length 3 exch sub { (0) exch concat2strings } repeat
	concat2strings
      } ifelse
    } forall
    (\) ) concat2strings
  } bind
  /operatortype	{ 64 string cvs cvn concat2strings } bind
  /nametype	{
    dup xcheck {
      dup length string cvs [ 3 1 roll ( ) ] concats
    } {
      dup length string cvs (/) exch [ 4 1 roll ( ) ] concats
    } ifelse
  } bind
  /realtype	{ 16 string cvs [ 3 1 roll ( ) ] concats } bind
  /integertype	{ 16 string cvs [ 3 1 roll ( ) ] concats } bind
  /booleantype	{ 16 string cvs [ 3 1 roll ( ) ] concats } bind
  /nulltype	{ pop (null ) concat2strings } bind
>> def
/concatanytostring { % str any concatanytostring string
  dup type concatanytostringprocs exch 2 copy known { get exec } {
    pop pop pop null concatanytostring
  } ifelse
} bind def

/cmap-dict-parser 26 dict dup begin
  /findresource { pop pop cmap-dict } bind def
  /begincmap { cmap-dict begin } bind def
  /endcmap { end } bind def
  /usecmap { /UsedCMapName exch def } bind def
  /beginrearrangedfont { pop pop mark } bind def
  /endrearrangedfont { cleartomark } bind def
  /begincodespacerange { pop mark } bind def
  /endcodespacerange { cleartomark } bind def
  /usefont { pop } bind def
  /beginbfchar { pop mark } bind def
  /endbfchar { cleartomark } bind def
  /beginbfrange { pop mark } bind def
  /endbfrange { cleartomark } bind def
  /begincidchar { pop mark } bind def
  /endcidchar { cleartomark } bind def
  /begincidrange { pop mark } bind def
  /endcidrange { cleartomark } bind def
  /beginnotdefchar { pop mark } bind def
  /endnotdefchar { cleartomark } bind def
  /beginnotdefrange { pop mark } bind def
  /endnotdefrange { cleartomark } bind def
  /beginusematrix { pop mark } bind def
  /endusematrix { cleartomark } bind def
  /StartData { pop pop ? } bind def	% not supported yet
  /defineresource { pop pop } bind def
end def

/cmap-dict-print { % - cmap-dict-print -
  detail {
    no-filename {} { (FileName:\t) cmap-dict /FileName get concat2strings = } ifelse
    [
      /CMapType
      /CMapName
      /CIDSystemInfo
      /CodeMap
      /CMapVersion
      /XUID
      /UIDOffset
      /WMode
      /UsedCMapName
    ] {
      dup cmap-dict exch known {
        dup dup length string cvs (:\t) concat2strings print%
        cmap-dict exch get () exch concatanytostring =
      } { pop } ifelse
    } forall
    () =
  } {
    no-cidsysteminfo not {
      cmap-dict /CIDSystemInfo get /Registry get =only (-) =only
      cmap-dict /CIDSystemInfo get /Ordering get =only (-) =only
      cmap-dict /CIDSystemInfo get /Supplement get =only (\t) =only
    } if
    cmap-dict /CMapName get =only (\t) =only
    cmap-dict /CMapVersion get =only
    no-filename { () = } { (\t) =only cmap-dict /FileName get = } ifelse
  } ifelse
} bind def

/cmap-dict-parse-file { % filename cmap-dict-parse-file -
  cmap-dict-parser begin
    /cmap-dict 13 dict def
    dup cmap-dict /FileName 3 -1 roll put
    run
    cmap-dict-print
  end
} bind def

/cmap-dict-registered-files 255 dict def
/cmap-dict-registered-obsolete-files 255 dict def
/cmap-dict-parse-file-register { % filename cmap-dict-parse-file-register -
  cmap-dict-parser begin
    /cmap-dict 13 dict def
    dup cmap-dict /FileName 3 -1 roll dup length string copy put
    run
    cmap-dict-registered-files cmap-dict /CMapName get known {
      cmap-dict-registered-files cmap-dict /CMapName get get /CMapVersion get
      cmap-dict /CMapVersion get 2 copy lt {
        pop pop
        cmap-dict-registered-obsolete-files cmap-dict-registered-files cmap-dict /CMapName get get dup /FileName get exch dup length dict copy put
        cmap-dict-registered-files cmap-dict /CMapName get
        cmap-dict dup length dict copy put
      } {
        gt {
          cmap-dict-registered-obsolete-files cmap-dict /FileName get
          cmap-dict dup length dict copy put
        } if
      } ifelse
    } {
      cmap-dict-registered-files cmap-dict /CMapName get
      cmap-dict dup length dict copy put
    } ifelse
  end
} bind def
/cmap-dict-print-registered-files { % - cmap-dict-print-registered-files -
  obsolete-only obsolete-info or { cmap-dict-registered-obsolete-files } { cmap-dict-registered-files } ifelse {
    exch pop /cmap-dict exch def
    obsolete-only obsolete-info or {
      obsolete-info {
        detail { (VersionStatus:\tObsolete) = } { (-) =only } ifelse
        cmap-dict-print
        cmap-dict-registered-files cmap-dict /CMapName get get /cmap-dict exch def
        detail { (VersionStatus:\tNewest) = } { (+) =only } ifelse
        cmap-dict-print
      } {
        cmap-dict-print
      } ifelse
    } { cmap-dict-print } ifelse
  } forall
} bind def

/cmap-dict-parse-here { % - cmap-dict-parse-here -
  /buffer 65535 string def
  {
    currentfile buffer readline not { pop exit } if
    register-mode { cmap-dict-parse-file-register } { cmap-dict-parse-file } ifelse
  } loop
  register-mode { cmap-dict-print-registered-files } if
} bind def
cmap-dict-parse-here
EOF
