-
Notifications
You must be signed in to change notification settings - Fork 0
/
tex2mbm.py
239 lines (213 loc) · 12 KB
/
tex2mbm.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#!/usr/bin/env python2
print "tex2mbm v1.121 (c) 2007 Silas S. Brown. License: Apache 2" # see below
import sys
# NB if you have more files than will fit on sys.argv,
# you can add to them here by doing something like
# import os ; sys.argv += filter(lambda x:x.startswith("myfile-"), os.listdir("."))
if len(sys.argv)<2:
print "Syntax: python2 tex2mbm.py input-files"
print "Input files can be .tex or .ps or .pdf"
print "CHANGE THE VARIABLES AT THE START OF THE SCRIPT FIRST."
print "Creates contents.dat, sequence.dat and *.mbm"
print "which should be loaded onto the EPOC device with MbmShow"
print "Each input file will be indexed in contents.dat, so you can choose a document by its number"
print "TeX files must not depend on anything else in the current directory, unless they specify it by absolute path"
sys.exit()
# ---- Change these variables : --------------
device_resolution = (640, 480) # for S7
# device_resolution = (480, 160) # for Revo
# bmconv_command = "Bmconv.exe"
bmconv_command = "wine Bmconv.exe"
# The following variables are used for setting the
# font size etc of TeX files, and are ignored for
# .ps files which the program assumes are already
# at your desired size :
lines_per_screen = 3 # number of lines you want
# to fit on the screen at a time
# (if not integer, leading can be added)
baseSize_points = 25 # the font size of the
# document at the moment (e.g. 25 if you're
# using \huge in 12pt, see latex-papersize.py)
documentClass = "\\documentclass[12pt]{article}"
max_symbol_height = 1.67 # the number of (document) lines
# that the biggest symbol will take - this will
# be used to make one screen line
path_to_latexPapersize = "/usr/local/bin/latex-papersize.py"
# Make sure to point this at a latex-papersize.py,
# must be at least version 1.4
just_make_PS = False # if True, will just make
# .ps files - this script can later be run again
# on those .ps files instead of the .tex files
# (but set ps_input_is_Whole_Slides = False below).
# This is useful if your machine is the only one
# that has CJK-LaTeX on it but you want to do
# the bitmap processing on a more powerful
# machine later.
ps_input_is_Whole_Slides = True # if True, assumes that,
# if there is no .tex input, then the input files (.ps
# and .pdf) are whole slides not 1 line only. Set this
# to False if the .ps files are from a just_make_PS run.
just_print_Bmconv_commands = False # if non-False,
# should be open("some-file","w") - will just
# print the bmconv.exe commands to that file,
# rather than trying to run them (and will not
# delete the temporary *.bmp files they need).
# Useful if this is running on a machine that is
# powerful for doing the bitmap work but that
# does not have bmconv.exe on it, and you want
# to run bmconv.exe later.
# --- End of variables that need changing -----
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Where to find history:
# on GitHub at https://github.com/ssb22/scan-reflow
# and on GitLab at https://gitlab.com/ssb22/scan-reflow
# and on BitBucket https://bitbucket.org/ssb22/scan-reflow
# and at https://gitlab.developers.cam.ac.uk/ssb22/scan-reflow
# and in China: https://gitee.com/ssb22/scan-reflow
import os, sys, zlib
baseFilename = "font"
if not ".tex" in ''.join(sys.argv):
startString = None
if ps_input_is_Whole_Slides:
lines_per_screen = 1
baseFilename = "slides" # call it slides.mbm rather than font.mbm
dpi_to_set_at = 100 # regardless of actual device DPI - be nice to metafont
papersize_px = (device_resolution[0],device_resolution[1]/lines_per_screen)
fontsize_px = papersize_px[1] / max_symbol_height
fontsize_pt = fontsize_px * 72.0 / dpi_to_set_at
papersize_mm = tuple(map(lambda x: x*25.4/dpi_to_set_at, papersize_px))
latex_paper_command = "margin_left=0 margin_top=0 paper_width="+str(papersize_mm[0])+" paper_height="+str(papersize_mm[1])+" python \""+path_to_latexPapersize+"\" "+str(baseSize_points)+" "+str(fontsize_pt)+" "
if ".tex" in ''.join(sys.argv):
# (note: don't need to do this if we're dealing with only .ps files)
assert open(path_to_latexPapersize), "path_to_latexPapersize does not appear to be set properly" # (more likely to raise IOError than AssertionError, but still)
startString = documentClass + os.popen(latex_paper_command+"tex").read()
startString += "\\usepackage[T1]{fontenc}" # hack to ensure uses bitmaps not outlines in teTeX 2 (not needed in teTeX 1) (TODO: will it work in teTeX 3?)
tempDir = os.popen("mktemp -d").read().strip()
datToCharNo = {} ; charNoToDat = {} # maps between bitmap data and character number
bmconv_params = []
if just_make_PS: seq=contents=None
else:
seq=open("sequence.dat","w")
contents = open("contents.dat","w")
oldDir = os.getcwd()
for inputFile in sys.argv[1:]:
try: dat = open(inputFile).read()
except IOError: dat = "" # the argument might be a scale factor, see below
os.chdir(tempDir)
gsInput = "tmp.ps"
if inputFile.endswith(".tex"):
for thing in "documentclass textwidth textheight topmargin marginparwidth oddsidemargin evensidemargin".split(): assert not "\\"+thing in dat, "TeX files must NOT contain \\"+thing+" (this will be added by the script)"
os.system("rm -f tmp.*")
open("tmp.tex","w").write(startString+dat)
ret = os.system("latex tmp.tex")
assert not ret, "TeX error"
ret = os.system(os.popen(latex_paper_command+"tmp.dvi").read().strip()+" -o tmp.ps -D "+str(dpi_to_set_at))
assert not ret, "dvips error"
elif inputFile.endswith(".ps") or inputFile.endswith(".pdf"):
if inputFile.endswith(".pdf"): gsInput="tmp.pdf"
open(gsInput,"w").write(dat)
else:
try: i=float(inputFile)
except: i=0
if i: # an extra scale factor for .ps input
assert not ".tex" in " ".join(sys.argv[1:]), "Scale factors on the command line should be used only with .ps or .pdf input. For scaling .tex input, change the variables at the start of the script."
dpi_to_set_at *= i # papersize is already ok
os.chdir(oldDir) ; continue
else: assert 0, "Extension of filename '"+inputFile+"' not supported"
if just_make_PS:
# just copy that .ps out, clean up, and don't do any more
open(oldDir+os.sep+inputFile[:inputFile.rfind(".")]+".ps","w").write(open("tmp.ps").read())
os.system("rm *") ; os.chdir(oldDir)
continue
# otherwise go ahead and make the bitmaps
print "Running gs to get PNGs"
ret = os.system("gs -sDEVICE=png16m -sOutputFile=tmp%%08d.png -g%dx%d -r%dx%d -q -dNOPAUSE - < %s" % (papersize_px[0],papersize_px[1],dpi_to_set_at,dpi_to_set_at,gsInput)) # need to write to png16m to stop awful dithering from some source PDFs when writing to png16 (e.g. Seamonkey output)
assert not ret, "gs error"
# Now look at those PNG files and add to the sequence ('seq') :
print "Examining PNGs"
def lsbmsb16(num): return chr(num%256)+chr(num/256)
def lsbmsb32(num): return lsbmsb16(num%65536)+lsbmsb16(num/65536)
if contents: contents.write(lsbmsb32(seq.tell()))
pngs = os.listdir(os.getcwd()) ; pngs.sort()
open("epoc16","w").write('P6\n16 1\n255\n\x00\x00\x00\x00\xff\xff\x00\xff\x00UUU\x88\x00\x00\x00\x00\x88\xaa\xaa\xaa\xff\x00\xff\xff\x00\x00\x99\x99\x00\x00\x99\x99\x99\x00\x99\xff\xff\xff\xff\xff\x00\x00\x88\x00\x00\x00\xff') # the 16 colours used by Sketch on S7 - probably safest to keep to those
open("epoc2","w").write('P6\n2 1\n255\n\x00\x00\x00\xff\xff\xff') # black & white
open("epoc4","w").write('P6\n4 1\n255\n\x00\x00\x00UUU\xaa\xaa\xaa\xff\xff\xff') # 4 greys
for f in pngs:
if not f.endswith(".png"): continue # ignore
dat=os.popen('pngtopnm "'+f+'" | pnmcrop -white -left -right -bottom | pnmremap -nofs -mapfile=epoc16 2>/dev/null').read() # (don't crop top because we're using it for alignment)
if not dat: continue # maybe it was a blank page (pnmcrop error) - ignore it
compressed_dat = zlib.compress(dat,9) # save VM
if not datToCharNo.has_key(compressed_dat): # new image
# work out how many colours we need. Don't use P6/P5/P4 because it often overstates things.
os.popen("pnmremap -nofs -mapfile=epoc4 2>/dev/null | pnmremap -nofs -mapfile=epoc16 >testfile 2>/dev/null","wb").write(dat)
if open("testfile").read()==dat:
# Can at least take it down to greymap. B&W ?
os.popen("pnmremap -nofs -mapfile=epoc2 2>/dev/null | pnmremap -nofs -mapfile=epoc16 >testfile 2>/dev/null","wb").write(dat)
if open("testfile").read()==dat: flag="/1" # B&W
else: flag="/2" # grey
else: flag="/c4" # 16 colours
fname="%08d.bmp" % len(datToCharNo)
bmconv_params.append(flag+fname)
charNoToDat[len(datToCharNo)]=compressed_dat
datToCharNo[compressed_dat]=len(datToCharNo)
seq.write(lsbmsb16(datToCharNo[compressed_dat]))
if contents: docs=contents.tell()/4
else: docs=1
print "Docs="+str(docs),"chars="+str(seq.tell()/2),"unique="+str(len(datToCharNo))
os.system("rm *") ; os.chdir(oldDir)
if just_make_PS:
os.system("rm -rf \"%s\"" % (tempDir,))
print "Made *.ps files - you now need to run this script on a more powerful machine, with just_make_PS and ps_input_is_Whole_Slides both set to False"
sys.exit()
AllUnique = (seq.tell()/2 == len(datToCharNo))
ContentsNotNeeded = (contents.tell() == 4) # only 1 document
del datToCharNo, contents, seq
# Finish by doing the conversion to MBM from the in-memory unique bitmaps :
startPoints=range(0,len(bmconv_params),510)+[len(bmconv_params)]
# (note that bmconv can't take more than 510 slides at a time - confirmed by using short filenames that this limit is in number of slides, not in number of characters on the command line)
os.chdir(tempDir)
for i in range(len(startPoints)-1):
for charNo in range(startPoints[i],startPoints[i+1]): os.popen("ppmtobmp > %08d.bmp" % (charNo,),"w").write(zlib.decompress(charNoToDat[charNo]))
if i==0: extra=""
else: extra=hex(i)[2:].upper() # (drop '0x' at beginning)
this_cmd = bmconv_command+" "+baseFilename+extra+".mbm "+' '.join(bmconv_params[startPoints[i]:startPoints[i+1]])
if just_print_Bmconv_commands: just_print_Bmconv_commands.write(this_cmd+"\n")
else:
ret=os.system(this_cmd)
assert not ret, "bmconv_command exitted with an error"
os.system("mv "+baseFilename+extra+".mbm \""+oldDir+"\" ; rm *")
os.chdir(oldDir)
# clean up, zip, print report
toPrint = ["\n--------------------------"] ; toZip = []
if not ContentsNotNeeded:
toPrint.append("Made contents.dat")
toZip.append("contents.dat")
AllUnique = False # because we DO make sequence.dat if we made contents.dat
else:
toPrint.append("Didn't make contents.dat, as there was only one input document")
os.remove("contents.dat")
if AllUnique:
toPrint.append("Didn't make sequence.dat, as all images were unique")
os.remove("sequence.dat")
if not AllUnique:
toPrint.append("Made sequence.dat")
toZip.append("sequence.dat")
if just_print_Bmconv_commands:
print "\n".join(toPrint)
print "Made bmconv commands (which should be run using the *.bmp files in %s)" % (tempDir,)
else:
os.system("rm -rf \"%s\"" % (tempDir,))
toPrint.append("Made "+baseFilename+"*.mbm")
toZip.append(baseFilename+"*.mbm")
assert not " " in baseFilename, "you'll be sorry..."
os.system("zip -9 to-epoc.zip "+" ".join(toZip)+" && rm "+" ".join(toZip))
print "\n".join(toPrint)
print "Zipped into to-epoc.zip for transfer to the device"