summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2008-05-25 00:25:23 +0000
committerKarl Berry <karl@freefriends.org>2008-05-25 00:25:23 +0000
commit7f5bad95abe8ecc801580c0420e9935969608be4 (patch)
tree0121f465e05c6154162e89a73da85d6e152e003f /Master/texmf-dist/scripts
parent30a8ff0f55da12b3e90f41d216406792379fd001 (diff)
new script dviasm by ChoF, documented in http://www.tug.org/TUGboat/Articles/tb28-2/tb89cho.pdf, source mirrored from http://svn.ktug.or.kr/viewvc/dviasm
git-svn-id: svn://tug.org/texlive/trunk@8329 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts')
-rwxr-xr-xMaster/texmf-dist/scripts/dviasm/dviasm.py951
1 files changed, 951 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/dviasm/dviasm.py b/Master/texmf-dist/scripts/dviasm/dviasm.py
new file mode 100755
index 00000000000..4fadc21e6dc
--- /dev/null
+++ b/Master/texmf-dist/scripts/dviasm/dviasm.py
@@ -0,0 +1,951 @@
+#! /usr/bin/env python
+# -*- coding: utf_8 -*-
+#
+# This is DVIasm, a DVI utility for editing DVI files directly.
+#
+# Copyright (C) 2007-2008 by Jin-Hwan Cho <chofchof@ktug.or.kr>
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import sys, os.path
+from optparse import OptionParser
+
+# Global variables
+is_ptex = False
+is_subfont = False
+cur_font = None
+cur_dsize = 0
+cur_ssize = 0
+subfont_idx = 0
+subfont_list = ['cyberb', 'outbtm', 'outbtb', 'outgtm', 'outgtb']
+
+# DVI opcodes
+SET_CHAR_0 = 0; SET_CHAR_127 = 127;
+SET1 = 128; SET2 = 129; SET3 = 130; SET4 = 131;
+SET_RULE = 132;
+PUT1 = 133; PUT2 = 134; PUT3 = 135; PUT4 = 136;
+PUT_RULE = 137;
+NOP = 138;
+BOP = 139; EOP = 140;
+PUSH = 141; POP = 142;
+RIGHT1 = 143; RIGHT2 = 144; RIGHT3 = 145; RIGHT4 = 146;
+W0 = 147; W1 = 148; W2 = 149; W3 = 150; W4 = 151;
+X0 = 152; X1 = 153; X2 = 154; X3 = 155; X4 = 156;
+DOWN1 = 157; DOWN2 = 158; DOWN3 = 159; DOWN4 = 160;
+Y0 = 161; Y1 = 162; Y2 = 163; Y3 = 164; Y4 = 165;
+Z0 = 166; Z1 = 167; Z2 = 168; Z3 = 169; Z4 = 170;
+FNT_NUM_0 = 171; FNT_NUM_63 = 234;
+FNT1 = 235; FNT2 = 236; FNT3 = 237; FNT4 = 238;
+XXX1 = 239; XXX2 = 240; XXX3 = 241; XXX4 = 242;
+FNT_DEF1 = 243; FNT_DEF2 = 244; FNT_DEF3 = 245; FNT_DEF4 = 246;
+PRE = 247; POST = 248; POST_POST = 249;
+# DVIV opcodes
+DIR = 255;
+# XDVI opcodes (not supported yet!)
+NATIVE_FONT_DEF = 250;
+PDF_FILE = 251; PIC_FILE = 252;
+GLYPH_ARRAY = 253; GLYPH_STRING = 254;
+# DVI identifications
+DVI_ID = 2; DVIV_ID = 3; XDVI_ID = 5;
+
+def Warning(msg):
+ sys.stderr.write('%s\n' % msg)
+
+def BadDVI(msg):
+ raise AttributeError, 'Bad DVI file: %s!' % msg
+
+def GetByte(fp): # { returns the next byte, unsigned }
+ try: return ord(fp.read(1))
+ except: return -1
+
+def SignedByte(fp): # { returns the next byte, signed }
+ try: b = ord(fp.read(1))
+ except: return -1
+ if b < 128: return b
+ else: return b - 256
+
+def Get2Bytes(fp): # { returns the next two bytes, unsigned }
+ try: a, b = map(ord, fp.read(2))
+ except: BadDVI('Failed to Get2Bytes()')
+ return (a << 8) + b
+
+def SignedPair(fp): # {returns the next two bytes, signed }
+ try: a, b = map(ord, fp.read(2))
+ except: BadDVI('Failed to SignedPair()')
+ if a < 128: return (a << 8) + b
+ else: return ((a - 256) << 8) + b
+
+def Get3Bytes(fp): # { returns the next three bytes, unsigned }
+ try: a, b, c = map(ord, fp.read(3))
+ except: BadDVI('Failed to Get3Bytes()')
+ return (((a << 8) + b) << 8) + c
+
+def SignedTrio(fp): # { returns the next three bytes, signed }
+ try: a, b, c = map(ord, fp.read(3))
+ except: BadDVI('Failed to SignedTrio()')
+ if a < 128: return (((a << 8) + b) << 8) + c
+ else: return ((((a - 256) << 8) + b) << 8) + c
+
+def SignedQuad(fp): # { returns the next four bytes, signed }
+ try: a, b, c, d = map(ord, fp.read(4))
+ except: BadDVI('Failed to get SignedQuad()')
+ if a < 128: return (((((a << 8) + b) << 8) + c) << 8) + d
+ else: return ((((((a - 256) << 8) + b) << 8) + c) << 8) + d
+
+def PutByte(q):
+ return chr(q & 0xff)
+
+def Put2Bytes(q):
+ return PutByte(q>>8) + PutByte(q)
+
+def Put3Bytes(q):
+ return PutByte(q>>16) + PutByte(q>>8) + PutByte(q)
+
+def PutSignedQuad(q):
+ if q < 0: q += 0x100000000
+ return PutByte(q>>24) + PutByte(q>>16) + PutByte(q>>8) + PutByte(q)
+
+def PutUnsigned(q):
+ if q >= 0x1000000: return (3, PutSignedQuad(q))
+ if q >= 0x10000: return (2, Put3Bytes(q))
+ if q >= 0x100: return (1, Put2Bytes(q))
+ return (0, PutByte(q))
+
+def PutSigned(q):
+ if 0 <= q < 0x800000: return PutUnsigned(q)
+ if q < -0x800000 or q >= 0x800000: return (3, PutSignedQuad(q))
+ if q < -0x8000: q += 0x1000000; return (2, Put3Bytes(q))
+ if q < -0x80: q += 0x10000; return (1, Put2Bytes(q))
+ return (0, PutByte(q))
+
+def GetInt(s):
+ try: return int(s)
+ except: return -1
+
+def GetStrASCII(s): # used in Parse()
+ if len(s) > 1 and ((s[0] == "'" and s[-1] == "'") or (s[0] == '"' and s[-1] == '"')): return [ord(c) for c in s[1:-1].decode('unicode_escape')]
+ else: return ''
+
+def UCS2toJIS(c):
+ s = c.encode('iso2022-jp')
+ if len(s) == 1: return ord(s)
+ else: return (ord(s[3]) << 8) + ord(s[4])
+
+def GetStrUTF8(s): # used in Parse()
+ if len(s) > 1 and ((s[0] == "'" and s[-1] == "'") or (s[0] == '"' and s[-1] == '"')):
+ t = s[1:-1].decode('string_escape').decode('utf8')
+ if is_ptex: return [UCS2toJIS(c) for c in t]
+ else: return [ord(c) for c in t]
+ else: return ''
+
+def PutStrASCII(t): # unsed in Dump()
+ s = ''
+ for o in t:
+ if o == 92: s += '\\\\'
+ elif 32 <= o < 127: s += chr(o)
+ elif o < 256: s += ('\\x%02x' % o)
+ elif o < 65536: s += ('\\u%04x' % o)
+ else:
+ Warning('Not support characters > 65535; may skip %d.\n' % o)
+ return "'%s'" % s
+
+def PutStrLatin1(t): # unsed in Dump()
+ s = ''
+ for o in t:
+ if o == 92: s += '\\\\'
+ elif 32 <= o < 127 or 161 <= o < 256: s += chr(o)
+ elif o < 256: s += ('\\x%02x' % o)
+ elif o < 65536: s += ('\\u%04x' % o)
+ else:
+ Warning('Not support characters > 65535; may skip %d.\n' % o)
+ return "'%s'" % s
+
+def PutStrUTF8(t): # unsed in Dump()
+ s = ''
+ if is_subfont:
+ for o in t:
+ s += unichr((subfont_idx << 8) + o).encode('utf8')
+ else: # not the case of subfont
+ for o in t:
+ if o == 92: s += '\\\\'
+ elif 32 <= o < 127: s += chr(o)
+ elif o < 128: s += ('\\x%02x' % o)
+ elif is_ptex:
+ s += ''.join(['\x1b$B', chr(o/256), chr(o%256)]).decode('iso2022-jp').encode('utf8')
+ else: s += unichr(o).encode('utf8')
+ return "'%s'" % s
+
+def PutStrSJIS(t): # unsed in Dump()
+ s = ''
+ for o in t:
+ if o == 92: s += '\\\\'
+ elif 32 <= o < 127: s += chr(o)
+ elif o < 128: s += ('\\x%02x' % o)
+ else:
+ s += ''.join(['\x1b$B', chr(o/256), chr(o%256)]).decode('iso2022-jp').encode('sjis')
+ return "'%s'" % s
+
+def IsFontChanged(f, z):
+ global cur_font, cur_ssize, subfont_idx, is_subfont
+ for n in subfont_list:
+ if n == f[:-2]:
+ is_subfont = True
+ subfont_idx = int(f[-2:], 16)
+ if cur_font == n and cur_ssize == z:
+ return False
+ else:
+ cur_font = n; cur_ssize = z
+ return True
+ else:
+ is_subfont = False
+ cur_font = f; cur_ssize = z
+ return True
+
+############################################################
+# DVI class
+############################################################
+class DVI(object):
+ def __init__(self, unit='pt'):
+ if unit == 'sp': self.byconv = self.by_sp_conv
+ elif unit == 'bp': self.byconv = self.by_bp_conv
+ elif unit == 'mm': self.byconv = self.by_mm_conv
+ elif unit == 'cm': self.byconv = self.by_cm_conv
+ elif unit == 'in': self.byconv = self.by_in_conv
+ else: self.byconv = self.by_pt_conv
+ self.Initialize()
+
+ ##########################################################
+ # Initialize: Required by __init__(), Load(), and Parse()
+ ##########################################################
+ def Initialize(self):
+ self.id = DVI_ID
+ self.numerator = 25400000
+ self.denominator = 473628672
+ self.mag = 1000
+ self.ComputeConversionFactors()
+ self.comment = ''
+ self.font_def = {}
+ self.max_v = self.max_h = self.max_s = self.total_pages = 0
+ self.pages = []
+
+ ##########################################################
+ # Load: DVI -> Internal Format
+ ##########################################################
+ def Load(self, fn):
+ fp = file(fn, 'rb')
+ self.LoadFromFile(fp)
+ fp.close()
+
+ def LoadFromFile(self, fp):
+ self.Initialize()
+ fp.seek(0, 2)
+ if fp.tell() < 53: BadDVI('less than 53 bytes long')
+ self.ProcessPreamble(fp)
+ self.ProcessPostamble(fp)
+ loc = self.first_backpointer
+ while loc >= 0:
+ fp.seek(loc)
+ if GetByte(fp) != BOP: BadDVI('byte %d is not bop' % fp.tell())
+ cnt = [SignedQuad(fp) for i in xrange(10)]
+ loc = SignedQuad(fp)
+ page = self.ProcessPage(fp)
+ self.pages.insert(0, {'count':cnt, 'content':page})
+
+ def ProcessPreamble(self, fp):
+ fp.seek(0)
+ if GetByte(fp) != PRE: BadDVI("First byte isn't start of preamble")
+ id = GetByte(fp)
+ if id != DVI_ID and id != DVIV_ID and id != XDVI_ID:
+ Warning("ID byte is %d; use the default %d!" % (id, DVI_ID))
+ else:
+ self.id = id
+ numerator = SignedQuad(fp)
+ if numerator <= 0:
+ Warning('numerator is %d; use the default 25400000!' % numerator)
+ else:
+ self.numerator = numerator
+ denominator = SignedQuad(fp)
+ if denominator <= 0:
+ Warning('denominator is %d; use the default 473628672!' % denominator)
+ else:
+ self.denominator = denominator
+ mag = SignedQuad(fp)
+ if mag <= 0:
+ Warning('magnification is %d; use the default 1000!' % mag)
+ else:
+ self.mag = mag
+ self.comment = fp.read(GetByte(fp))
+ self.ComputeConversionFactors()
+
+ def ProcessPostamble(self, fp):
+ fp.seek(-5, 2) # at least four 223's
+ while True:
+ k = GetByte(fp)
+ if k < 0: BadDVI('all 223s; is it a DVI file?') # found EOF
+ elif k != 223: break
+ fp.seek(-2, 1)
+ if k != DVI_ID and k != DVIV_ID and k != XDVI_ID:
+ Warning('ID byte is %d' % k)
+ fp.seek(-5, 1)
+ q = SignedQuad(fp)
+ m = fp.tell() # id_byte
+ if q < 0 or q > m - 33: BadDVI('post pointer %d at byte %d' % (q, m - 4))
+ fp.seek(q) # move to post
+ k = GetByte(fp)
+ if k != POST: BadDVI('byte %d is not post' % k)
+ self.post_loc = q
+ self.first_backpointer = SignedQuad(fp)
+
+ if SignedQuad(fp) != self.numerator:
+ Warning("numerator doesn't match the preamble!")
+ if SignedQuad(fp) != self.denominator:
+ Warning("denominator doesn't match the preamble!")
+ if SignedQuad(fp) != self.mag:
+ Warning("magnification doesn't match the preamble!")
+ self.max_v = SignedQuad(fp)
+ self.max_h = SignedQuad(fp)
+ self.max_s = Get2Bytes(fp)
+ self.total_pages = Get2Bytes(fp)
+ while True:
+ k = GetByte(fp)
+ if k == FNT_DEF1: p = GetByte(fp)
+ elif k == FNT_DEF2: p = Get2Bytes(fp)
+ elif k == FNT_DEF3: p = Get3Bytes(fp)
+ elif k == FNT_DEF4: p = SignedQuad(fp)
+ elif k != NOP: break
+ self.DefineFont(p, fp)
+ if k != POST_POST:
+ Warning('byte %d is not postpost!' % (fp.tell() - 1))
+ if SignedQuad(fp) != self.post_loc:
+ Warning('bad postamble pointer in byte %d!' % (fp.tell() - 4))
+ m = GetByte(fp)
+ if m != DVI_ID and m != DVIV_ID and m != XDVI_ID:
+ Warning('identification in byte %d should be %d, %d, or %d!' % (fp.tell() - 1, DVI_ID, DVIV_ID, XDVI_ID))
+
+ def DefineFont(self, e, fp):
+ c = SignedQuad(fp) # font_check_sum
+ q = SignedQuad(fp) # font_scaled_size
+ d = SignedQuad(fp) # font_design_size
+ n = fp.read(GetByte(fp) + GetByte(fp))
+ try:
+ f = self.font_def[e]
+ except KeyError:
+ self.font_def[e] = {'name':n, 'checksum':c, 'scaled_size':q, 'design_size':d}
+ if q <= 0 or q >= 01000000000:
+ Warning("%s---not loaded, bad scale (%d)!" % (n, q))
+ elif d <= 0 or d >= 01000000000:
+ msssage("%s---not loaded, bad design size (%d)!" % (n, d))
+ else:
+ if f['checksum'] != c:
+ Warning("\t---check sum doesn't match previous definition!")
+ if f['scaled_size'] != q:
+ Warning("\t---scaled size doesn't match previous definition!")
+ if f['design_size'] != d:
+ Warning("\t---design size doesn't match previous definition!")
+ if f['name'] != n:
+ Warning("\t---font name doesn't match previous definition!")
+
+ def ProcessPage(self, fp):
+ s = []
+ while True:
+ o = GetByte(fp)
+ p = self.Get1Arg(o, fp)
+ if o < SET_CHAR_0 + 128 or o in (SET1, SET2, SET3, SET4):
+ q = [p]
+ while True:
+ o = GetByte(fp)
+ p = self.Get1Arg(o, fp)
+ if o < SET_CHAR_0 + 128 or o in (SET1, SET2, SET3, SET4):
+ q.append(p)
+ else:
+ break
+ s.append([SET1, q])
+ if o == SET_RULE:
+ s.append([SET_RULE, [p, SignedQuad(fp)]])
+ elif o in (PUT1, PUT2, PUT3, PUT4):
+ s.append([PUT1, p])
+ elif o == PUT_RULE:
+ s.append([PUT_RULE, [p, SignedQuad(fp)]])
+ elif o == NOP:
+ continue
+ elif o == BOP:
+ Warning('bop occurred before eop!')
+ break
+ elif o == EOP:
+ break
+ elif o == PUSH:
+ s.append([PUSH])
+ elif o == POP:
+ s.append([POP])
+ elif o in (RIGHT1, RIGHT2, RIGHT3, RIGHT4):
+ s.append([RIGHT1, p])
+ elif o == W0:
+ s.append([W0])
+ elif o in (W1, W2, W3, W4):
+ s.append([W1, p])
+ elif o == X0:
+ s.append([X0])
+ elif o in (X1, X2, X3, X4):
+ s.append([X1, p])
+ elif o in (DOWN1, DOWN2, DOWN3, DOWN4):
+ s.append([DOWN1, p])
+ elif o == Y0:
+ s.append([Y0])
+ elif o in (Y1, Y2, Y3, Y4):
+ s.append([Y1, p])
+ elif o == Z0:
+ s.append([Z0])
+ elif o in (Z1, Z2, Z3, Z4):
+ s.append([Z1, p])
+ elif o < FNT_NUM_0 + 64 or o in (FNT1, FNT2, FNT3, FNT4):
+ s.append([FNT1, p])
+ elif o in (XXX1, XXX2, XXX3, XXX4):
+ q = fp.read(p)
+ s.append([XXX1, q])
+ elif o in (FNT_DEF1, FNT_DEF2, FNT_DEF3, FNT_DEF4):
+ self.DefineFont(p, fp)
+ elif o == DIR:
+ s.append([DIR, p])
+ elif o == PRE:
+ Warning('preamble command within a page!')
+ break
+ elif o in (POST, POST_POST):
+ Warning('postamble command %d!' % o)
+ break
+ else:
+ Warning('undefined command %d!' % o)
+ break
+ return s
+
+ def Get1Arg(self, o, fp):
+ if o < SET_CHAR_0 + 128:
+ return o - SET_CHAR_0
+ if o in (SET1, PUT1, FNT1, XXX1, FNT_DEF1, DIR):
+ return GetByte(fp)
+ if o in (SET2, PUT2, FNT2, XXX2, FNT_DEF2):
+ return Get2Bytes(fp)
+ if o in (SET3, PUT3, FNT3, XXX3, FNT_DEF3):
+ return Get3Bytes(fp)
+ if o in (RIGHT1, W1, X1, DOWN1, Y1, Z1):
+ return SignedByte(fp)
+ if o in (RIGHT2, W2, X2, DOWN2, Y2, Z2):
+ return SignedPair(fp)
+ if o in (RIGHT3, W3, X3, DOWN3, Y3, Z3):
+ return SignedTrio(fp)
+ if o in (SET4, SET_RULE, PUT4, PUT_RULE, RIGHT4, W4, X4, DOWN4, Y4, Z4, FNT4, XXX4, FNT_DEF4):
+ return SignedQuad(fp)
+ if o in (NOP, BOP, EOP, PUSH, POP, PRE, POST, POST_POST) or o > POST_POST:
+ return 0
+ if o in (W0, X0, Y0, Z0):
+ return 0
+ if o < FNT_NUM_0 + 64:
+ return o - FNT_NUM_0
+
+ ##########################################################
+ # Save: Internal Format -> DVI
+ ##########################################################
+ def Save(self, fn):
+ fp = file(fn, 'wb')
+ self.SaveToFile(fp)
+ fp.close()
+
+ def SaveToFile(self, fp):
+ # WritePreamble
+ fp.write(''.join([chr(PRE), PutByte(self.id), PutSignedQuad(self.numerator), PutSignedQuad(self.denominator), PutSignedQuad(self.mag), PutByte(len(self.comment)), self.comment]))
+ # WriteFontDefinitions
+ self.WriteFontDefinitions(fp)
+ # WritePages
+ stackdepth = 0; loc = -1
+ for page in self.pages:
+ w = x = y = z = 0; stack = []
+ s = [chr(BOP)]
+ s.extend([PutSignedQuad(c) for c in page['count']])
+ s.append(PutSignedQuad(loc))
+ for cmd in page['content']:
+ if cmd[0] == SET1:
+ for o in cmd[1]:
+ if o < 128: s.append(chr(SET_CHAR_0 + o))
+ else: s.append(self.CmdPair([SET1, o]))
+ elif cmd[0] in (SET_RULE, PUT_RULE):
+ s.append(chr(cmd[0]) + PutSignedQuad(cmd[1][0]) + PutSignedQuad(cmd[1][1]))
+ elif cmd[0] == PUT1:
+ s.append(self.CmdPair([PUT1, cmd[1][0]]))
+ elif cmd[0] in (RIGHT1, DOWN1):
+ s.append(self.CmdPair(cmd))
+ elif cmd[0] in (W0, X0, Y0, Z0):
+ s.append(chr(cmd[0]))
+ elif cmd[0] == PUSH:
+ s.append(chr(PUSH))
+ stack.append((w, x, y, z))
+ if len(stack) > stackdepth: stackdepth = len(stack)
+ elif cmd[0] == POP:
+ s.append(chr(POP))
+ w, x, y, z = stack.pop()
+ elif cmd[0] == W1:
+ w = cmd[1]; s.append(self.CmdPair(cmd))
+ elif cmd[0] == X1:
+ x = cmd[1]; s.append(self.CmdPair(cmd))
+ elif cmd[0] == Y1:
+ y = cmd[1]; s.append(self.CmdPair(cmd))
+ elif cmd[0] == Z1:
+ z = cmd[1]; s.append(self.CmdPair(cmd))
+ elif cmd[0] == FNT1:
+ if cmd[1] < 64: s.append(chr(FNT_NUM_0 + cmd[1]))
+ else: s.append(self.CmdPair(cmd))
+ elif cmd[0] == XXX1:
+ l = len(cmd[1])
+ if l < 256: s.append(chr(XXX1) + chr(l) + cmd[1])
+ else: s.append(chr(XXX4) + PutSignedQuad(l) + cmd[1])
+ elif cmd[0] == DIR:
+ s.append(chr(DIR) + chr(cmd[1]))
+ else:
+ Warning('invalid command %s!' % cmd[0])
+ s.append(chr(EOP))
+ loc = fp.tell()
+ fp.write(''.join(s))
+ # WritePostamble
+ post_loc = fp.tell()
+ fp.write(''.join([chr(POST), PutSignedQuad(loc), PutSignedQuad(self.numerator), PutSignedQuad(self.denominator), PutSignedQuad(self.mag), PutSignedQuad(self.max_v), PutSignedQuad(self.max_h), Put2Bytes(stackdepth+1), Put2Bytes(len(self.pages))]))
+ # WriteFontDefinitions
+ self.WriteFontDefinitions(fp)
+ # WritePostPostamble
+ fp.write(''.join([chr(POST_POST), PutSignedQuad(post_loc), PutByte(self.id), '\xdf\xdf\xdf\xdf']))
+ loc = fp.tell()
+ while (loc % 4) != 0:
+ fp.write('\xdf'); loc += 1
+
+ def WriteFontDefinitions(self, fp):
+ s = []
+ for e in sorted(self.font_def.keys()):
+ l, q = PutUnsigned(e)
+ s.append(PutByte(FNT_DEF1 + l))
+ s.append(q)
+ s.append(PutSignedQuad(self.font_def[e]['checksum']))
+ s.append(PutSignedQuad(self.font_def[e]['scaled_size']))
+ s.append(PutSignedQuad(self.font_def[e]['design_size']))
+ s.append('\x00')
+ s.append(PutByte(len(self.font_def[e]['name'])))
+ s.append(self.font_def[e]['name'])
+ fp.write(''.join(s))
+
+ def CmdPair(self, cmd):
+ l, q = PutSigned(cmd[1])
+ return chr(cmd[0] + l) + q
+
+ ##########################################################
+ # Parse: Text -> Internal Format
+ ##########################################################
+ def Parse(self, fn, encoding=''):
+ fp = file(fn, 'r')
+ s = fp.read()
+ fp.close()
+ self.ParseFromString(s, encoding=encoding)
+
+ def ParseFromString(self, s, encoding=''):
+ global GetStr, cur_font, cur_dsize, cur_ssize, subfont_idx
+ if encoding == 'ascii': GetStr = GetStrASCII
+ else: GetStr = GetStrUTF8
+ self.Initialize()
+ self.fnt_num = 0
+ for l in s.split('\n'):
+ l = l.strip()
+ if not l or l[0] == '%': continue
+ try:
+ key, val = l.split(':', 1)
+ key = key.strip(); val = val.strip()
+ except:
+ if l[-1] == ']': v = l[:-1].split(' ')
+ else: v = l.split(' ')
+ if v[0] == "[page":
+ self.cur_page = []
+ count = [GetInt(c) for c in v[1:]]
+ if len(count) < 10: count += ([0] * (10-len(count)))
+ self.pages.append({'count':count, 'content':self.cur_page})
+ continue
+ # ParsePreamble
+ if key == "id":
+ self.id = GetInt(val)
+ if self.id != DVI_ID and self.id != DVIV_ID and self.id != XDVI_ID:
+ Warning("identification byte should be %d, %d, or %d!" % (DVI_ID, DVIV_ID, XDVI_ID))
+ elif key == "numerator":
+ d = GetInt(val)
+ if d <= 0:
+ Warning('non-positive numerator %d!' % d)
+ else:
+ self.numerator = d
+ self.ComputeConversionFactors()
+ elif key == "denominator":
+ d = GetInt(val)
+ if d <= 0:
+ Warning('non-positive denominator %d!' % d)
+ else:
+ self.denominator = d
+ self.ComputeConversionFactors()
+ elif key == "magnification":
+ d = GetInt(val)
+ if d <= 0:
+ Warning('non-positive magnification %d!' % d)
+ else:
+ self.mag = d
+ elif key == "comment":
+ self.comment = val[1:-1]
+ # Parse Postamble
+ elif key == "maxv":
+ self.max_v = self.ConvLen(val)
+ elif key == "maxh":
+ self.max_h = self.ConvLen(val)
+ elif key == "maxs":
+ self.max_s = GetInt(val)
+ elif key == "pages":
+ self.total_pages = GetInt(val)
+ # Parse Font Definitions
+ elif key == "fntdef":
+ n, q, d = self.GetFntDef(val)
+ self.font_def[self.fnt_num] = {'name':n, 'design_size':d, 'scaled_size':q, 'checksum':0}
+ self.fnt_num += 1
+ # Parse Pages
+ elif key == 'xxx':
+ self.cur_page.append([XXX1, eval(val)])
+ elif key == 'set':
+ ol = GetStr(val)
+ if is_subfont:
+ subfont_idx = (ol[0] >> 8)
+ self.AppendFNT1()
+ nl = [ol[0] & 0xff]
+ for o in ol[1:]:
+ idx = (o >> 8)
+ if idx != subfont_idx:
+ self.cur_page.append([SET1, nl])
+ subfont_idx = idx
+ self.AppendFNT1()
+ nl = [o & 0xff]
+ else:
+ nl.append(o & 0xff)
+ self.cur_page.append([SET1, nl])
+ else:
+ self.cur_page.append([SET1, ol])
+ elif key == 'put':
+ self.cur_page.append([PUT1, GetStr(val)])
+ elif key == 'setrule':
+ v = val.split(' ')
+ if len(v) != 2:
+ Warning('two values are required for setrule!')
+ continue
+ self.cur_page.append([SET_RULE, [self.ConvLen(c) for c in v]])
+ elif key == 'putrule':
+ v = val.split(' ')
+ if len(v) != 2:
+ Warning('two values are required for putrule!')
+ continue
+ self.cur_page.append([PUT_RULE, [self.ConvLen(c) for c in v]])
+ elif key == 'fnt':
+ n, q, d = self.GetFntDef(val)
+ if n in subfont_list:
+ is_subfont = True
+ cur_font = n; cur_dsize = d; cur_ssize = q
+ else:
+ is_subfont = False
+ f = {'name':n, 'design_size':d, 'scaled_size':q, 'checksum':0}
+ try:
+ e = self.font_def.keys()[self.font_def.values().index(f)]
+ except:
+ e = self.fnt_num
+ self.font_def[self.fnt_num] = f
+ self.fnt_num += 1
+ self.cur_page.append([FNT1, e])
+ elif key == 'right':
+ self.cur_page.append([RIGHT1, self.ConvLen(val)])
+ elif key == 'down':
+ self.cur_page.append([DOWN1, self.ConvLen(val)])
+ elif key == 'w':
+ self.cur_page.append([W1, self.ConvLen(val)])
+ elif key == 'x':
+ self.cur_page.append([X1, self.ConvLen(val)])
+ elif key == 'y':
+ self.cur_page.append([Y1, self.ConvLen(val)])
+ elif key == 'z':
+ self.cur_page.append([Z1, self.ConvLen(val)])
+ elif key == 'push':
+ self.cur_page.append([PUSH])
+ elif key == 'pop':
+ self.cur_page.append([POP])
+ elif key == 'w0':
+ self.cur_page.append([W0])
+ elif key == 'x0':
+ self.cur_page.append([X0])
+ elif key == 'y0':
+ self.cur_page.append([Y0])
+ elif key == 'z0':
+ self.cur_page.append([Z0])
+ elif key == 'dir':
+ self.cur_page.append([DIR, GetInt(val)])
+ else:
+ Warning('invalid command %s!' % key)
+
+ def AppendFNT1(self):
+ f = {'name':cur_font+"%02x"%subfont_idx, 'design_size':cur_dsize, 'scaled_size':cur_ssize, 'checksum':0}
+ try:
+ e = self.font_def.keys()[self.font_def.values().index(f)]
+ except:
+ e = self.fnt_num
+ self.font_def[e] = f
+ self.fnt_num += 1
+ self.cur_page.append([FNT1, e])
+
+ ##########################################################
+ # Dump: Internal Format -> Text
+ ##########################################################
+ def Dump(self, fn, tabsize=2, encoding=''):
+ fp = file(fn, 'w')
+ self.DumpToFile(fp, tabsize=tabsize, encoding=encoding)
+ fp.close()
+
+ def DumpToFile(self, fp, tabsize=2, encoding=''):
+ global PutStr
+ if encoding == 'ascii': PutStr = PutStrASCII
+ elif encoding == 'latin1': PutStr = PutStrLatin1
+ elif encoding == 'sjis': PutStr = PutStrSJIS
+ else: PutStr = PutStrUTF8
+ # DumpPreamble
+ fp.write("[preamble]\n")
+ fp.write("id: %d\n" % self.id)
+ fp.write("numerator: %d\n" % self.numerator)
+ fp.write("denominator: %d\n" % self.denominator)
+ fp.write("magnification: %d\n" % self.mag)
+ fp.write("comment: %s\n" % repr(self.comment))
+ # DumpPostamble
+ fp.write("\n[postamble]\n")
+ fp.write("maxv: %s\n" % self.byconv(self.max_v))
+ fp.write("maxh: %s\n" % self.byconv(self.max_h))
+ fp.write("maxs: %d\n" % self.max_s)
+ fp.write("pages: %d\n" % self.total_pages)
+ # DumpFontDefinitions
+ fp.write("\n[font definitions]\n")
+ for e in sorted(self.font_def.keys()):
+ fp.write("fntdef: %s " % self.font_def[e]['name'])
+ if self.font_def[e]['design_size'] != self.font_def[e]['scaled_size']:
+ fp.write("(%s) " % self.by_pt_conv(self.font_def[e]['design_size']))
+ fp.write("at %s\n" % self.by_pt_conv(self.font_def[e]['scaled_size']))
+ # DumpPages
+ for page in self.pages:
+ fp.write("\n[page" + (" %d"*10 % tuple(page['count'])) + "]\n")
+ indent = 0
+ for cmd in page['content']:
+ if cmd[0] == POP:
+ indent -= tabsize
+ fp.write("%spop:\n" % (' ' * indent))
+ continue
+ fp.write("%s" % (' ' * indent))
+ if cmd[0] == PUSH:
+ fp.write("push:\n")
+ indent += tabsize
+ elif cmd[0] == XXX1:
+ fp.write("xxx: %s\n" % repr(cmd[1]))
+ elif cmd[0] == DIR:
+ fp.write("dir: %d\n" % cmd[1])
+ elif cmd[0] == SET_RULE:
+ fp.write("setrule: %s %s\n" % (self.byconv(cmd[1][0]), self.byconv(cmd[1][1])))
+ elif cmd[0] == PUT_RULE:
+ fp.write("putrule: %s %s\n" % (self.byconv(cmd[1][0]), self.byconv(cmd[1][1])))
+ elif cmd[0] == SET1:
+ fp.write("set: %s\n" % PutStr(cmd[1]))
+ elif cmd[0] == PUT1:
+ fp.write("put: %s\n" % PutStr(cmd[1]))
+ elif cmd[0] == FNT1:
+ f = self.font_def[cmd[1]]['name']
+ z = self.font_def[cmd[1]]['scaled_size']
+ if IsFontChanged(f, z):
+ fp.write("fnt: %s " % cur_font)
+ if self.font_def[cmd[1]]['design_size'] != self.font_def[cmd[1]]['scaled_size']:
+ fp.write("(%s) " % self.by_pt_conv(self.font_def[cmd[1]]['design_size']))
+ fp.write("at %s\n" % self.by_pt_conv(cur_ssize))
+ elif cmd[0] == RIGHT1:
+ fp.write("right: %s\n" % self.byconv(cmd[1]))
+ elif cmd[0] == DOWN1:
+ fp.write("down: %s\n" % self.byconv(cmd[1]))
+ elif cmd[0] == W1:
+ fp.write("w: %s\n" % self.byconv(cmd[1]))
+ elif cmd[0] == X1:
+ fp.write("x: %s\n" % self.byconv(cmd[1]))
+ elif cmd[0] == Y1:
+ fp.write("y: %s\n" % self.byconv(cmd[1]))
+ elif cmd[0] == Z1:
+ fp.write("z: %s\n" % self.byconv(cmd[1]))
+ elif cmd[0] == W0:
+ fp.write("w0:\n")
+ elif cmd[0] == X0:
+ fp.write("x0:\n")
+ elif cmd[0] == Y0:
+ fp.write("y0:\n")
+ elif cmd[0] == Z0:
+ fp.write("z0:\n")
+
+ ##########################################################
+ # Misc Functions
+ ##########################################################
+ def ComputeConversionFactors(self):
+ self.sp_conv = (self.numerator / 25400000.) * (473628672. / self.denominator)
+ self.pt_conv = (self.numerator / 25400000.) * (7227. / self.denominator)
+ self.bp_conv = (self.numerator / 254000.) * (72. / self.denominator)
+ self.mm_conv = (self.numerator / 10000.) / self.denominator
+ self.cm_conv = (self.numerator / 100000.) / self.denominator
+ self.in_conv = (self.numerator / 254000.) * (1. / self.denominator)
+
+ def ConvLen(self, s):
+ try: return int(s)
+ except: pass
+ try: f = float(s[:-2])
+ except: return 0
+ m = s[-2:]
+ if m == "pt": return int(round(f / self.pt_conv))
+ elif m == "in": return int(round(f / self.in_conv))
+ elif m == "mm": return int(round(f / self.mm_conv))
+ elif m == "cm": return int(round(f / self.cm_conv))
+ elif m == "bp": return int(round(f / self.bp_conv))
+ elif m == "sp": return int(round(f / self.sp_conv))
+ else:
+ try: return int(round(f / self.pt_conv))
+ except: return 0
+
+ def GetFntDef(self, s):
+ try:
+ n, size = s.split('(', 1)
+ d, q = size.split(')', 1)
+ except:
+ n, q = s.split(' ', 1)
+ n = n.strip(); q = q.strip()
+ if q[:2] == "at": q = q[2:]
+ q = self.ConvLen(q.strip())
+ try: d = self.ConvLen(d.strip())
+ except: d = q
+ return n, q, d
+
+ def by_sp_conv(self, a):
+ v = self.sp_conv * a
+ return "%dsp" % int(v)
+
+ def by_pt_conv(self, a):
+ v = self.pt_conv * a
+ if v == int(v): return "%dpt" % int(v)
+ else: return "%fpt" % v
+
+ def by_bp_conv(self, a):
+ v = self.bp_conv * a
+ if v == int(v): return "%dbp" % int(v)
+ else: return "%fbp" % v
+
+ def by_mm_conv(self, a):
+ v = self.mm_conv * a
+ if v == int(v): return "%dmm" % int(v)
+ else: return "%fmm" % v
+
+ def by_cm_conv(self, a):
+ v = self.cm_conv * a
+ if v == int(v): return "%dcm" % int(v)
+ else: return "%fcm" % v
+
+ def by_in_conv(self, a):
+ v = self.in_conv * a
+ if v == int(v): return "%din" % int(v)
+ else: return "%fin" % v
+
+############################################################
+# Misc Functions for Main Routine
+############################################################
+def ProcessOptions():
+ usage = "%prog [options] dvi_file|dvi_dump_file"
+ version = """This is %prog-20080520 by Jin-Hwan Cho (Korean TeX Society)
+
+Copyright (C) 2007-2008 by Jin-Hwan Cho <chofchof@ktug.or.kr>
+
+This 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
+(at your option) any later version."""
+ parser = OptionParser(usage=usage, version=version)
+ parser.add_option("-u", "--unit",
+ action="store", type="string", dest="unit",
+ metavar="STR",
+ help="unit (sp, pt, bp, mm, cm, in) [default=%default]")
+ parser.add_option("-o", "--output",
+ action="store", type="string", dest="output",
+ metavar="FILE",
+ help="filename for output instead of stdout")
+ parser.add_option("-e", "--encoding",
+ action="store", type="string", dest="encoding",
+ metavar="STR",
+ help="encoding for input/output [default=%default]")
+ parser.add_option("-t", "--tabsize",
+ action="store", type="int", dest="tabsize",
+ metavar="INT",
+ help="tab size for push/pop [default=%default]")
+ parser.add_option("-p", "--ptex",
+ action="store_true", dest="ptex", default=False,
+ help="extended DVI for Japanese pTeX")
+ parser.add_option("-s", "--subfont",
+ action="append", type="string", dest="subfont",
+ metavar="STR",
+ help="the list of fonts with UCS2 subfont scheme (comma separated); disable internal subfont list if STR is empty")
+ parser.set_defaults(unit='pt', encoding='utf8', tabsize=2)
+ (options, args) = parser.parse_args()
+ if not options.unit in ['sp', 'pt', 'bp', 'mm', 'cm', 'in']:
+ parser.error("invalid unit name '%s'!" % options.unit)
+ if options.tabsize < 0:
+ parser.error("negative tabsize!")
+ if not options.encoding in ['ascii', 'latin1', 'utf8', 'sjis']:
+ parser.error("invalid encoding '%s'!" % options.encoding)
+ if options.ptex:
+ global is_ptex
+ is_ptex = True
+ if not options.encoding in ['utf8', 'sjis']:
+ parser.error("invalid encoding '%s' for Japanese pTeX!" % options.encoding)
+ if options.subfont:
+ global subfont_list
+ if not options.subfont[0]: # disable subfont
+ subfont_list = []
+ for l in options.subfont:
+ subfont_list.extend([f.strip() for f in l.split(',')])
+ if len(args) != 1:
+ parser.error("try with the option --help!")
+ return (options, args)
+
+def IsDVI(fname):
+ from os.path import splitext
+ if splitext(fname)[1] != '.dvi': return False
+ try:
+ fp = file(fname, 'rb')
+ fp.seek(0)
+ if GetByte(fp) != PRE: return False
+ fp.seek(-4, 2)
+ if GetByte(fp) != 223: return False
+ fp.close()
+ except:
+ sys.stderr.write('Failed to read %s\n' % fname)
+ return False
+ return True
+
+############################################################
+# Main Routine
+############################################################
+if __name__ == '__main__':
+ (options, args) = ProcessOptions()
+ aDVI = DVI(unit=options.unit)
+ if IsDVI(args[0]): # dvi -> dump
+ aDVI.Load(args[0])
+ if options.output: aDVI.Dump(options.output, tabsize=options.tabsize, encoding=options.encoding)
+ else: aDVI.DumpToFile(sys.stdout, tabsize=options.tabsize, encoding=options.encoding)
+ else: # dump -> dvi
+ aDVI.Parse(args[0], encoding=options.encoding)
+ if options.output: aDVI.Save(options.output)
+ else: aDVI.SaveToFile(sys.stdout)