#!/usr/local/bin/python2.7
################################################################################
# bg5ps.py which use the ttf2ps program to convert the Big5 Coding chinese     #
# text into printable postscript file.  Since it uses true type font, the      #
# quality is much better than using bitmap fonts.                              #
#******************************************************************************#
#
# $Log: bg5ps,v $
# Revision 1.6  2000/02/14 20:46:43  platin
# small fix about option name typo...
#
# Revision 1.5  2000/02/14 20:40:04  platin
#
# Change default config file to /usr/local/lib/bg5ps/bg5ps.conf
#
# Revision 1.4  2000/02/14 20:28:35  platin
# Automatic selection of fontName in echo Encoding.
#
# Revision 1.3  2000/02/14 19:07:15  platin
# Adjusting the config files.
#
# Revision 1.2  2000/02/14 18:54:18  platin
#
# Modify to support GB2312.
# FIXME: Must change the range of picking up multibyte chars, LATER!!
#
#
#******************************************************************************#
# bg5ps.py ver. 1.1b2 Date:Oct 1 98                                            #
# Copyright (C) 1998  by Chen-Shan Chin                                        #
# 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 2 of the License, or any later version.  #
#                                                                              #
# This program is distributed in the hope that it will be useful, but WITHOUT  #
# ANY WARRANTY; without even implied warranty of MERCHANTABILITY of FITTNESS   #
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more        # 
# details.                                                                     #
#                                                                              #
# You should have received a copy of the GNU General Public License along      #
# with this program; if not, write to the Free Sofeware Foundation, Inc., 675  #
# Mass Ave, Cambridge, MA 02139, USA                                           #
################################################################################

import sys,os

#Program global variables
true=1
false=0
#wrongChineseCodingError='wrongChineseCodingError'
bg5ChrDict={}
wd=os.path.dirname(sys.argv[0])
if wd=="": wd="."

#start default config file segment
#Font and font path
chineseFontPath=wd
fontName="bsmi00lp"

#Output only even or odd
oddPages=true
evenPages=true

#size and margins are in unit of pt
size=12.0

#page layout
topMargin=72.0
bottomMargin=72.0
leftMargin=72.0
rightMargin=72.0

#paragraph parameter in pt
lineSpace=6.0
chrSpace=2.0

inputFileName=""
outputFileName=""
configFile=os.path.expanduser("~")+".bg5ps.conf"
ttf2psmPath=wd
#end config file segment

#default: no netscape or mpage mode
npsMode="n"

argSetup={"-fp":["chineseFontPath","string"],
	  "-fn":["fontName","string"],
	  "-o":["oddPages","int"],
	  "-e":["evenPages","int"],
	  "-s":["size","float"],
	  "-tm":["topMargin","float"],
	  "-bm":["bottomMargin","float"],
	  "-lm":["leftMargin","float"],
	  "-rm":["rightMargin","float"],
	  "-if":["inputFileName","string"],
	  "-of":["outputFileName","string"],
	  "-ls":["lineSpace","float"],
	  "-cs":["chrSpace","float"],
          "-cf":["configFile","string"],
	  "-en":["Encoding","string"],
          "-nps":["npsMode","string"]}


def argvParser(ps,usage):
    from string import atoi,atof
    rtn={}
    for i in range(1,len(sys.argv),2):
        if sys.argv[i] in ps.keys():
            if ps[sys.argv[i]][1]=="int":
                try:
                    rtn[ps[sys.argv[i]][0]]=atoi(sys.argv[i+1])
                except ValueError:
                    sys.stderr.write("\nValueError on option %s\n" % sys.argv[i])
		    sys.stderr.write(usage)
                    sys.exit(1)

            elif ps[sys.argv[i]][1]=="float":
                try:
                    rtn[ps[sys.argv[i]][0]]=atof(sys.argv[i+1])
                except ValueError:
                    sys.stderr.write("\nValueError on option %s\n" % sys.argv[i])
		    sys.stderr.write(usage)
                    sys.exit(1)

            elif ps[sys.argv[i]][1]=="string":
                rtn[ps[sys.argv[i]][0]]=sys.argv[i+1]
        else:
	    if sys.argv[i]!="-h":
		sys.stderr.write("Wrong option %s\n" % sys.argv[i])
	    sys.stderr.write(usage)
            sys.exit(1)
    return rtn

def getChr16(cc):
    c1=ord(cc[0])
    c2=ord(cc[1])
    return c1*256+c2

def getOffset(cc):
    c1=ord(cc[0])
    c2=ord(cc[1])
    return c1*256+c2
#    if c1 >= 0xC9:
#	c=(c1-0xC9)*157+5401
#        sys.stderr.write(\
#"Warning: Non common character used(%s %s). \n\
#The result may not be right for some fonts.\n" % (hex(c1*256+c2),chr(c1)+chr(c2)))
#    elif c1 >= 0xA4:
#	c=(c1-0xA4)*157
#    else:
#	c=(c1-0xA1)*157+13094
#
#    if c2<128:
#	c=c+c2-64
#    else:
#	c=c+c2-161+63
#    return c

def scanFile():
    from string import rstrip
    nl=1
    for l_orig in bg5FileLines:
        l=rstrip(l_orig)
	i=0
        ln=len(l)
	while i < ln:
            if i+1 < ln and \
               (ord(l[i]) >= 161 and ord(l[i]) <= 249) and \
               ((ord(l[i+1]) >=64 and ord(l[i+1]) <=126) or \
                (ord(l[i+1]) >=161 and ord(l[i+1]) <=254)) : # bg5_c
                cnchr=l[i:i+2]
                offset=getOffset(cnchr)
                if not bg5ChrDict.has_key(cnchr):
                    bg5ChrDict[cnchr]=offset
                i=i+1
	    i=i+1

def npsModeScanFile():
    import regex
    sp=regex.compile('[^\]?(\(.*\)[^\]?) show')
    for l in bg5FileLines:
	if sp.search(l)!=-1:
	    cs=eval('""" '+sp.group(1)+' """') # magic code
	    i=0
            ln=len(cs)
	    while i < ln:
                if i+1 < ln and \
                   (ord(cs[i]) >= 161 and ord(cs[i]) <= 249) and \
                   ((ord(cs[i+1]) >=64 and ord(cs[i+1]) <=126) or \
                    (ord(cs[i+1]) >=161 and ord(cs[i+1]) <=254)): #bg5_c
                    cnchr=cs[i:i+2]
                    offset=getOffset(cnchr)
                    if not bg5ChrDict.has_key(cnchr):
                        bg5ChrDict[cnchr]=offset
                    i=i+1
	        i=i+1
    
def genPSF():
    if not os.path.exists(ttf2psmPath+"/ttf2psm"):
	sys.stderr.write("\nError:"+ttf2psmPath+"/ttf2psm doesn't exist\n")
	sys.exit(2)

    if not os.path.exists(fontFileName):
	sys.stderr.write("\nError: Font file %s not found.\n\n" % fontFileName)
	sys.exit(2)

    import tempfile
    tmpfilename=tempfile.mktemp()
    tmpfile=open(tmpfilename,"w")
    for i in bg5ChrDict.keys():
	tmpoffset=bg5ChrDict[i]
	if tmpoffset!=13094: #Big white space, no font data
	    tmpfile.write("%d\n" % tmpoffset)
    tmpfile.write("-1\n")
    tmpfile.close()
    sys.stderr.write("\nrunning "+ttf2psmPath+"/"+ttf2psmcommand+" %s < %s\n\n" % (fontFileName,tmpfilename))
    inpipe=os.popen(ttf2psmPath+"/"+ttf2psmcommand+" %s < %s" % (fontFileName,tmpfilename))
    rtnlist=inpipe.readlines()
#Reported as a bug here for inpipe.close(), comment it. Trying to figure why.
#    inpipe.close()
    os.system("rm %s" % tmpfilename)
    return rtnlist

def genbg5Dict():
    ow=outfile.write
    ow("/bg5dict <<\n")
    for ch in bg5ChrDict.keys():
        ow("%d" % getChr16(ch))
        ow("(w%d)\n" % bg5ChrDict[ch])
    ow(">> def\n")

    ow(\
"/dbg5w {/csize currentfont /ScaleMatrix get 0 get def "+\
"gsave "+\
"csize csize pt "+\
"currentpoint translate gsave "+\
"bg5dict exch get cvx exec grestore "+\
"grestore currentpoint exch csize add exch moveto " +\
"} bind def")


def genModShow():
    outfile.write(\
"""
/show {
/str exch def
/i 0 def
str length /ls exch def
{ i 1 add ls ge {exit} if
  str i get /c1 exch def
  str i 1 add get /c2 exch def  
  c1 161 ge c1 249 le and  
  c2 64 ge c2 126 le and  
  c2 161 ge c2 254 le and or and
  {/c c1 256 mul c2 add def c dbg5w /i i 2 add def} 
  { str i 1 getinterval show /i i 1 add def}
  ifelse
}
loop
i 1 add ls eq {str i 1 getinterval show} if
} bind def
""")

def genPS():
    from string import rstrip, expandtabs
    scanFile()
    if bg5FileLines==[]:
	sys.stderr.write("\nError: Empty input file\n\n")
	sys.exit(2)

    #begin debug code
    #print string.joinfields(bg5FileLines,"")
    #print bg5FileLines
    #print bg5ChrDict
    #end debug code
    ow=outfile.write
    ow(\
"""%!PS-Adobe-2.0
%%Creator: bg5ps
%%Pages: (atend)
%%BeginProlog
% English font
/Courier findfont """+\
"12 scalefont setfont"+\
"""/pt{/tmp exch def 12.0 div tmp 12.0 div scale} bind def /tr{translate} def """+\
"/gl{%.2f 0 tr} def " % (12.0+chrSpace/size*12.0,) +\
"/egl{%.2f 0 tr} def " % (6.0+chrSpace/size*6.0,)+\
"""/m{moveto} def /s{-1 2 m show} def 12 12 pt""")

    for l in genPSF():
	outfile.write(l+" ")

    ow(\
"""
%%EndProlog
%%BeginSetup
%%EndSetup
""")

    sys.stderr.write("Totally %d different Chinese characters have been processed.\n\n" % len(bg5ChrDict))

    newpage=true
    np=1
    gnp=0
    nl=1
    y=0.0

    for l_orig in bg5FileLines:
        l=rstrip(expandtabs(l_orig))
	i=0
	x=0.0
	dc=("\n","\t","\\","(",")")
	allowed=(oddPages==true and np % 2 ==1) or (evenPages==true and np % 2==0)
	if newpage==true:
	    if allowed:
                ow("%%" + "Page: %d %d\n" % (np,np))
		ow("%%BeginPageSetup\n")
		ow("/pgsave save def ")	    
		ow("gsave /Utopia findfont 9 scalefont setfont ")
                ow("380 48 translate 0 0 moveto ")
                ow("(printed by bg5ps ver. 1.1b2 page: %d) show grestore " % np)
		ow("%.2f %.2f translate " % (leftMargin,792-topMargin-size))
		ow("gsave ")
		ow("%.3f %.3f pt\n" % (size,size))
		ow("%%EndPageSetup\n")
	    newpage=false
        ln=len(l)
	while i < ln:
            if i+1 < ln and \
               (ord(l[i]) >= 161 and ord(l[i]) <= 249) and \
               ((ord(l[i+1]) >=64 and ord(l[i+1]) <=126) or \
                (ord(l[i+1]) >=161 and ord(l[i+1]) <=254)) : # bg5_c
                cnchr=l[i:i+2]
                offset=getOffset(cnchr)
                if offset==13094: #big space
                    if allowed:
                        ow(" gl")
                    x=x+12.0+chrSpace/size*12.0
                else:
                    if allowed:
                        ow(" w%d gl" % offset)
                    x=x+12.0+chrSpace/size*12.0
                i=i+1
	    elif not l[i] in ("\n","(",")","\\"):
		if allowed:
		    ow(" (%s)s egl" % l[i])
		x=x+6.0+chrSpace/size*6.0
	    elif l[i] in ("(", ")","\\"):
		if allowed:
		    ow(" (\%s)s egl" % l[i])
		x=x+6.0+chrSpace/size*6.0

	    if x >= (610.0-leftMargin-rightMargin-size)*12.0/size:
		if allowed:
		    ow(" %.2f %.2f tr" % (-x,-12-lineSpace/size*12))
		y=y+12.0+lineSpace/size*12
		x=0.0

	    i=i+1
	ow("\n")
	nl=nl+1
	if allowed:
	    ow(" %.2f %.2f tr" % (-x,-12-lineSpace/size*12))
	y=y+12+lineSpace/size*12
	if y>(792.0-topMargin-bottomMargin-size)*12.0/size:
	    if allowed:
		ow(" grestore pgsave restore showpage\n")
		gnp=gnp+1
	    nl=1
	    y=0.0
	    newpage=true
	    np=np+1
	x=0.0

    if allowed:
	ow(" pgsave restore showpage\n")
	gnp=gnp+1
    ow("\n%%Trailer\n")
    ow("%%Pages: "+"%d\n" % gnp)
    ow("%%EOF\n")
    sys.stderr.write("%d of %d pages postscript file has been generated.\n" % (gnp,np))

#########################################################################################
# main

if __name__ == '__main__':
    sys.stderr.write("bg5ps version:1.1b2\nCopyright (C) 1998  Chen-Shan Chin\n")
    sys.stderr.write("bg5ps comes with ABSOULETY NO WARRANTY, see GNU General Public License for detail.\n")

    usage=\
"""
bg5ps -fp fontpath -fn fontname 
      -o [1|0] -e [1|0] 
      -s size -ls linespacing -cs charspacing 
      -tm topMargin -bm bottomMargin -lm leftMargin -rm rightMargin
      -if inputfile -of outputfile 
      -cf configFile
      -en encoding
      -nps y/n
      
Size, linespacing, charspacing, and margins are 
numbers in unit of pt(1 pt=1/72 in).

encoding can be big5 or gb2312.

bg5ps -h: print this help
bg5ps -nps y: toggle netscape or mpage mode

Run as a filter:
cat big5text | bg5ps options > outputfile
 
Examples:

bg5ps -en big5 -fn ntu_kai -o 1 -e 0 -s 15 -ls 10 -cs 2 -tm 72 -bm 72 -if test -of test.ps

cat netscape.ps | bg5ps -en big5 -nps y -fn ntu_kai > test.ps

mpage -2 big5.txt | bg5ps -en big5 -nps y > test.ps
"""

#get command options
    cmdOpts=argvParser(argSetup,usage)

#get config file
    if cmdOpts.has_key("configFile"):
        if cmdOpts["configFile"]!="":
            configFile=cmdOpts["configFile"]
            if os.path.isfile(configFile):
                sys.stderr.write("Use configuration file %s.\n" % configFile)
                cf=open(configFile,"r")
                for l in cf.readlines():
                    exec l
                cf.close()
            else:
                sys.stderr.write("Error: Wrong configuration file name'%s'\n" % configFile)
                sys.stderr.write("       Use default configuration instead\n")
    else:    
        sys.stderr.write("\nNo configuration file option is given.\n Trying ~/.bg5ps.conf")
        if os.path.isfile(os.path.expanduser("~")+"/.bg5ps.conf"):
            sys.stderr.write("\n~/.bg5ps.conf found. Use configuration file ~/.bg5ps.conf")
            cf=open(os.path.expanduser("~")+"/.bg5ps.conf","r")
            for l in cf.readlines():
                exec l
            cf.close()
        else:
            sys.stderr.write("\n~/.bg5ps.conf not found.\n Trying /usr/local/lib/bg5ps/bg5ps.conf.")
            if os.path.isfile("/usr/local/lib/g5ps/bg5ps.conf"):
                sys.stderr.write("\n /usr/local/lib/bg5ps/bg5ps.conf found. Use configuration file /usr/local/lib/bg5ps/bg5ps.conf")
                cf=open("/usr/local/lib/bg5ps/bg5ps.conf","r")
                for l in cf.readlines():
                    exec l
                cf.close()
            else:
                sys.stderr.write("\nNo configuration file found. Use default configuration.")

#get options
    for i in cmdOpts.keys():
        exec i+"=cmdOpts[i]"

#encoding
    if Encoding=="big5":
		ttf2psmcommand="ttf2psm -b"
		l="fontName=fontName_"+Encoding
		exec l
    elif Encoding=="gb2312":
		ttf2psmcommand="ttf2psm -g"
		l="fontName=fontName_"+Encoding
		exec l
    else:
		sys.stderr.write("\nError: Wrong encoding '%s'!!\n\n" % Encoding)
		sys.exit(2)

    fontFileName=chineseFontPath+"/%s" % fontName

    if inputFileName=="":
        bg5File=sys.stdin
        sys.stderr.write("\nReading file from stdin....\n")
    else:
        try:
            bg5File=open(inputFileName,"r")
        except IOError:
            sys.stderr.write("\nError: No such file '%s'\n\n" % inputFileName)
            sys.exit(2)

    if outputFileName=="":
        outfile=sys.stdout
    else:
        outfile=open(outputFileName,"w")

    bg5FileLines=bg5File.readlines()

    if npsMode!="y":
        genPS()
    else:
        import regex
        npsModeScanFile()
        bp=regex.compile("%%EndProlog")
        ow=outfile.write
        for l in bg5FileLines:        
            if bp.search(l)==-1:
                ow(l),
            else:
                ow("%%Creator: bg5ps(nps/mpage mode)\n")
                for ll in genPSF():
                    ow(ll),
                ow("/pt{/tmp exch def 12.0 div tmp 12.0 div scale} bind def\n")
                ow("/w13094 {} def\n")
                genbg5Dict()
                genModShow()
                ow(l),

    bg5File.close()
    outfile.close()
