1
0
mirror of https://github.com/ssb22/scan-reflow.git synced 2023-06-16 10:02:22 +00:00
scan-reflow/tex2mbm.py

240 lines
12 KiB
Python

#!/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"