Linux ip-172-26-7-228 5.4.0-1103-aws #111~18.04.1-Ubuntu SMP Tue May 23 20:04:10 UTC 2023 x86_64
Your IP : 3.144.227.3
#! /usr/bin/env python2.7
# -*- 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>
# Copyright (C) 2011-2017 by Khaled Hosny <khaledhosny@eglug.org>
#
# 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;
# DVI-IVD opcodes
BEGIN_REFLECT = 250; END_REFLECT = 251;
# XDV opcodes
NATIVE_FONT_DEF = 252;
GLYPHS = 253;
TEXT_GLYPHS = 254;
# XDV flags
XDV_FLAG_VERTICAL = 0x0100;
XDV_FLAG_COLORED = 0x0200;
XDV_FLAG_EXTEND = 0x1000;
XDV_FLAG_SLANT = 0x2000;
XDV_FLAG_EMBOLDEN = 0x4000;
# DVI identifications
DVI_ID = 2; DVIV_ID = 3; XDVI_ID = 6; XDV_ID = 7;
DVI_IDS = (DVI_ID, DVIV_ID, XDVI_ID, XDV_ID)
def warning(msg):
sys.stderr.write('%s\n' % msg)
def ValidID(dvi_id):
if dvi_id not in DVI_IDS:
return False
return True
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 Get4Bytes(fp): # { returns the next four bytes, unsigned }
try: a, b, c, d = map(ord, fp.read(4))
except: BadDVI('Failed to Get4Bytes()')
return (((((a << 8) + b) << 8) + c) << 8) + d
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 PutGlyphs(width, glyphs):
s = []
length = len(glyphs)
s.append(PutSignedQuad(width))
s.append(Put2Bytes(length))
for glyph in glyphs:
s.append(PutSignedQuad(glyph["x"]))
s.append(PutSignedQuad(glyph["y"]))
for glyph in glyphs:
s.append(Put2Bytes(glyph["id"]))
return ''.join(s)
def PutTextGlyphs(text, width, glyphs):
s = []
length = len(text)
s.append(Put2Bytes(length))
for ch in text:
s.append(Put2Bytes(ch))
s.append(PutGlyphs(width, glyphs))
return ''.join(s)
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 not ValidID(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 not ValidID(k):
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 == NATIVE_FONT_DEF: p = SignedQuad(fp)
elif k != NOP: break
if k == NATIVE_FONT_DEF: self.DefineNativeFont(p, fp)
else: 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 not ValidID(m):
warning('identification in byte %d should be one of: %s!' % (fp.tell() - 1, DVI_IDS))
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:
warning("%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 DefineNativeFont(self, e, fp):
size = Get4Bytes(fp) # scaled size
flags = Get2Bytes(fp)
l = GetByte(fp) # name length
fnt_name = fp.read(l)
index = Get4Bytes(fp) # face index
ext = []
embolden = 0
if flags & XDV_FLAG_VERTICAL: ext.append("vertical")
if flags & XDV_FLAG_COLORED: ext.append("color=%08X" % Get4Bytes(fp))
if flags & XDV_FLAG_EXTEND: ext.append("extend=%d" % SignedQuad(fp))
if flags & XDV_FLAG_SLANT: ext.append("slant=%d" % SignedQuad(fp))
if flags & XDV_FLAG_EMBOLDEN: ext.append("embolden=%d" % SignedQuad(fp))
try:
f = self.font_def[e]
except KeyError:
if index > 0:
fnt_name += "[%d]" % index
name = '"%s"' % fnt_name
if ext:
name = '"%s:%s"' % (fnt_name, ";".join(ext))
self.font_def[e] = {
'name': name,
'checksum': 0,
'scaled_size': size,
'design_size': 655360, # hardcoded
}
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 == NATIVE_FONT_DEF:
self.DefineNativeFont(p, fp)
elif o == GLYPHS:
s.append([GLYPHS, self.GetGlyphs(fp)])
elif o == TEXT_GLYPHS:
s.append([TEXT_GLYPHS, self.GetTextGlyphs(fp)])
elif o == DIR:
s.append([DIR, p])
elif o == BEGIN_REFLECT:
s.append([BEGIN_REFLECT])
elif o == END_REFLECT:
s.append([END_REFLECT])
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, NATIVE_FONT_DEF):
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, BEGIN_REFLECT, END_REFLECT):
return 0
if o < FNT_NUM_0 + 64:
return o - FNT_NUM_0
def GetGlyphs(self, fp):
width = SignedQuad(fp)
length = Get2Bytes(fp)
glyphs = {}
for i in range(length):
glyphs[i] = {}
glyphs[i]["x"] = SignedQuad(fp)
glyphs[i]["y"] = SignedQuad(fp)
for i in range(length):
glyphs[i]["id"] = Get2Bytes(fp)
return (width, glyphs)
def GetTextGlyphs(self, fp):
length = Get2Bytes(fp)
chars = []
for i in range(length):
chars.append(Get2Bytes(fp))
width, glyphs = self.GetGlyphs(fp)
return (chars, width, glyphs)
def ReadGlyphs(self, val):
import re
glyphs = []
w, g = val.split(" ", 1)
for m in re.finditer(r"gid(?P<id>\d+?)\((?P<pos>.*?.)\)", g):
gid = m.group("id")
pos = m.group("pos")
if "," in pos:
x, y = pos.split(",")
else:
x, y = pos, "0sp"
glyphs.append({"id": int(gid), 'x': self.ConvLen(x), 'y': self.ConvLen(y)})
return (self.ConvLen(w), glyphs)
def ReadTextGlyphs(self, val):
_, text, glyphs = val.split(val[0])
text = "'%s'" % text
glyphs = glyphs.lstrip()
chars = GetStr(text)
w, glyphs = self.ReadGlyphs(glyphs)
return (chars, w, glyphs)
##########################################################
# 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]))
elif cmd[0] == BEGIN_REFLECT:
s.append(chr(BEGIN_REFLECT))
elif cmd[0] == END_REFLECT:
s.append(chr(END_REFLECT))
elif cmd[0] == GLYPHS:
s.append(PutByte(GLYPHS))
s.append(PutGlyphs(cmd[1], cmd[2]))
elif cmd[0] == TEXT_GLYPHS:
s.append(PutByte(TEXT_GLYPHS))
s.append(PutTextGlyphs(cmd[1], cmd[2], cmd[3]))
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()):
if self.font_def[e]['native']:
flags = self.font_def[e]['flags']
s.append(PutByte(NATIVE_FONT_DEF))
s.append(PutSignedQuad(e))
s.append(PutSignedQuad(self.font_def[e]['scaled_size']))
s.append(Put2Bytes(flags))
s.append(PutByte(len(self.font_def[e]['name'])))
s.append(self.font_def[e]['name'])
s.append(PutSignedQuad(self.font_def[e]['index']))
print >> sys.stderr, self.font_def[e]['name'], self.font_def[e]['index']
if flags & XDV_FLAG_COLORED: s.append(PutSignedQuad(self.font_def[e]['color']))
if flags & XDV_FLAG_EXTEND: s.append(PutSignedQuad(self.font_def[e]['extend']))
if flags & XDV_FLAG_SLANT: s.append(PutSignedQuad(self.font_def[e]['slant']))
if flags & XDV_FLAG_EMBOLDEN: s.append(PutSignedQuad(self.font_def[e]['embolden']))
else:
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 not ValidID(self.id):
warning('identification byte %d should be one of: %s!' % (self.id, DVI_IDS))
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":
self.font_def[self.fnt_num] = self.GetFntDef(val)
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':
f = self.GetFntDef(val)
n = f['name']
d = f['design_size']
q = f['scaled_size']
if n in subfont_list:
is_subfont = True
cur_font = n; cur_dsize = d; cur_ssize = q
else:
is_subfont = False
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)])
elif key == 'begin_reflect':
self.cur_page.append([BEGIN_REFLECT])
elif key == 'end_reflect':
self.cur_page.append([END_REFLECT])
elif key == 'setglyphs':
w, glyphs = self.ReadGlyphs(val)
self.cur_page.append([GLYPHS, w, glyphs])
elif key == 'settextglyphs':
text, w, glyphs = self.ReadTextGlyphs(val)
self.cur_page.append([TEXT_GLYPHS, text, w, glyphs])
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] == BEGIN_REFLECT:
fp.write("begin_reflect:\n")
elif cmd[0] == END_REFLECT:
fp.write("end_reflect:\n")
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] == GLYPHS:
fp.write("setglyphs: %s\n" % self.DumpGlyphs(cmd[1][0], cmd[1][1]))
elif cmd[0] == TEXT_GLYPHS:
fp.write("settextglyphs: %s\n" % self.DumpTextGlyphs(cmd[1][0], cmd[1][1], cmd[1][2]))
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")
def DumpGlyphs(self, w, g):
yPresent = False
for i in g:
if g[i]["y"] != 0:
yPresent = True
glyphs = []
for i in g:
gid = "gid%s" % g[i]["id"]
x = self.byconv(g[i]["x"])
y = self.byconv(g[i]["y"])
if yPresent:
glyphs.append("%s(%s, %s)" % (gid, x, y))
else:
glyphs.append("%s(%s)" % (gid, x))
return "%s %s" % (self.byconv(w), " ".join(glyphs))
def DumpTextGlyphs(self, t, w, g):
return "%s %s" % (PutStrUTF8(t), self.DumpGlyphs(w, g))
##########################################################
# 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):
f = {}
try:
n, size = s.split('(', 1)
d, q = size.split(')', 1)
except:
n, q = s.split(' ', 1)
n = n.strip(); q = q.strip()
if n.startswith('"') and n.endswith('"'):
f['native'] = True
n = n.strip('"')
flags = 0
color = 0
extend = 0
slant = 0
embolden = 0
try:
name, ext = n.split(':')
except:
name, ext = n, ""
try:
name, index = name.split('[')
index = index.split(']')[0]
except:
index = 0
if ext:
ext = ext.split(';')
for opt in ext:
try:
key, value = opt.split('=')
except:
key, value = opt, ""
if key == "color":
flags |= XDV_FLAG_COLORED
color = int(value, 16)
if key == "vertical":
flags |= XDV_FLAG_VERTICAL
if key == "extend":
flags |= XDV_FLAG_EXTEND
extend = int(value)
if key == "slant":
flags |= XDV_FLAG_SLANT
slant = int(value)
if key == "embolden":
flags |= XDV_FLAG_EMBOLDEN
embolden = int(value)
f['name'] = name
f['index'] = int(index)
f['flags'] = flags
f['color'] = color
f['extend'] = extend
f['slant'] = slant
f['embolden'] = embolden
else:
f['native'] = False
f['name'] = n
if q[:2] == "at": q = q[2:]
q = self.ConvLen(q.strip())
try: d = self.ConvLen(d.strip())
except: d = q
f['design_size'] = d
f['scaled_size'] = q
f['checksum'] = 0
return f
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
DVIasm is a Python script to support changing or creating DVI files
via disassembling into text, editing, and then reassembling into
binary format. It is fully documented at
http://tug.org/TUGboat/Articles/tb28-2/tb89cho.pdf
http://ajt.ktug.kr/assets/2008/5/1/0201cho.pdf"""
version = """This is %prog-20171216 by Jin-Hwan Cho (Korean TeX Society)
Copyright (C) 2007-2008 by Jin-Hwan Cho <chofchof@ktug.or.kr>
Copyright (C) 2011-2017 by Khaled Hosny <khaledhosny@eglug.org>
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 3 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] not in ('.dvi', '.xdv'): 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)
|