]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi.py
release: 1.1.24
[lilypond.git] / scripts / ly2dvi.py
1 #!@PYTHON@
2
3 """
4 =======================================================================
5 LilyPond to dvi converter
6
7 Features include Title information, paper size specification, and image
8 orientation.  
9
10 Usage: ly2dvi.py [OPTION]... [FILE]...
11 Input: LilyPond source or LilyPond generated TeX files
12 Output: DVI file
13 =======================================================================
14 """
15
16 name = 'ly2dvi'
17 version = '0.0.11'
18 errorlog = ''
19
20 import sys
21 import os
22 import getopt
23 import re
24 import string
25 import time
26 import glob
27
28
29 class Input:
30     """
31     This class handles all ly2dvi.py input file methods
32
33     Public methods:
34     
35     __init__()  Constructor
36     open(file)  Open a .ly file or .tex file using lilyinclude path
37     close()     Close current file
38     type()      Determine file type .ly (input) or .tex (output)
39     setVars()   Set title definitions found in .tex (output) file
40     """
41
42     #
43     # Constructors
44     #
45
46     def __init__(this):
47        this.__fd = None 
48
49     #
50     # open
51     #
52     def open(this,file):
53         """
54         open file and set private class variable __fd.  The search
55         sequence is: current directory followed by the directories
56         found in include property list.  Each directory is searched
57         for file, file.ly, and file.fly.
58         
59         input:  file   filename
60         output: void
61         error:  ExitNotFound Exception
62         """
63
64         for i in [''] + Props.get('include')[0:]:
65             ifile = os.path.join(i,file)
66             for j in ['','.ly','.fly']:
67                 jfile = ifile+j
68                 try:
69                     this.__fd = open( jfile, 'r' )
70                     return
71                 except:
72                     pass
73         sys.exit('ExitNotFound', file)
74
75
76     #
77     # close
78     #
79     def close(this):
80         """
81         close file object __fd
82         
83         input:  void
84         output: void
85         error:  None
86         """
87         this.__fd.close()
88
89
90     #
91     # type
92     #
93     def type(this):
94         """
95         Determine input file type.  LilyPond source is 'input' type
96         and LilyPond generated TeX file is 'output' type
97
98         input:  void
99         output: 'input' | 'output'
100         error:  None
101         """
102
103         firstline = this.__fd.readline()
104         this.__fd.seek(0)
105         if  re.match('%created by: GNU LilyPond [0-9]+[.0-9]+',firstline ):
106             return 'output'
107         else:
108             return 'source'
109
110
111     #
112     # setVars
113     #
114     def setVars(this):  
115         """
116         Search for properties in the current input file and set the
117         appropriate values.  The supported properties names are in
118         local variable varTable along with the property list
119         titledefs.
120
121         input:  void
122         output: None
123         error:  None
124         """
125
126         varTable = [
127             #   regexp              set method
128             #   ------              ----------
129             ( 'language',        Props.setLanguage ),
130             ( 'latexheaders',    Props.setHeader ),
131             ( 'orientation',     Props.setOrientation ),
132             ( 'paperpapersize',  Props.setPaperZize ),
133             ( 'papertextheight', Props.setTextHeight ),
134             ( 'paperlinewidth',  Props.setLineWidth ),
135             ( 'filename',        Props.setFilename ),
136             ]
137
138         titles={}
139         for line in this.__fd.readlines():
140             m=re.match('\\\\def\\\\mudela([\w]+){(.*)}',line)
141             if m:
142                 for var in varTable:
143                     if m.group(1) == var[0]:
144                         var[1](m.group(2),'file')
145                         break
146                 for var in Props.get('titledefs'):
147                     if m.group(1) == var:
148                         titles[var]=m.group(2)
149                         break
150         Props.setTitles(titles,'file')
151         this.__fd.seek(0)
152
153 \f
154
155 class TeXOutput:
156     """
157     This class handles all ly2dvi.py output file methods
158
159     private methods:
160      __mudelaDefs(opt)  Send title info to output file
161
162     Public methods:
163     __init__()  Constructor
164     write(str)  Write a string to output file 
165     start(file) Start the latex file
166     next()      Process next output file
167     end()       Finish latex file and run latex 
168     """
169
170     #
171     # constructor
172     #
173     def __init__(this):
174        this.__fd = None 
175        this.__base = ''
176        this.__outfile = ''
177
178     #
179     # __medelaDefs
180     #
181     def __mudelaDefs(this,opt):
182         """
183         Write titles to output
184
185         input:  opt   Supports header and subheader output
186         output: None
187         error:  None
188         """
189
190         titles = Props.get('titles')
191         for key in titles.keys():
192             this.write('%s\\mudela%s{%s}%%\n' % (opt,key,titles[key]))
193
194     #
195     # write
196     #
197     def write(this,str):
198         """
199         Write str to current output file
200
201         input:  str  String to write
202         output: None
203         error:  None
204         """
205         
206         this.__fd.write(str)
207
208     #
209     # start
210     #
211     def start(this,file):
212         """
213         Start LaTeX file.  Calculates the horizontal and vertical
214         margin using pagewidth, pageheight, linewidth, and textheight.
215         Creates temporary output filename and opens it for write.
216         Sends the LaTeX header information to output.  Lastly sends
217         the title information to output.
218
219         input:  file  output file name 
220         output: None
221         error:  None
222         """
223
224         now=time.asctime(time.localtime(time.time()))
225         linewidth = Props.get('linewidth')
226         textheight = Props.get('textheight')
227
228         if Props.get('orientation') == 'landscape':
229             pagewidth = Props.get('pageheight')
230             pageheight = Props.get('pagewidth')
231         else:
232             pageheight = Props.get('pageheight')
233             pagewidth = Props.get('pagewidth')
234                                  
235         horizontalMarginArg =  ( (pagewidth - linewidth)/2 )   
236         verticalMarginArg =  ( (pageheight - textheight)/2  )
237
238         top="""\
239 %% Creator: %s
240 %% Automatically generated from  %s, %s
241
242 \\documentclass[%s]{article}
243
244 %s 
245 \\usepackage{geometry}
246 \\usepackage[latin1]{inputenc} 
247 %%\\usepackage[T1]{fontenc} 
248 %s 
249 %%\\addtolength{\\oddsidemargin}{-1cm} 
250 %%\\addtolength{\\topmargin}{-1cm} 
251 %%\\setlength{\\textwidth}{%s} 
252 %%\\setlength{\\textheight}{%s} 
253 \\geometry{width=%spt, left=%spt, height=%spt, top=%spt} 
254 \\input lilyponddefs 
255 \\input titledefs 
256 %s 
257 \\begin{document}
258 """ % ( program_id(), Props.get('filename'), now, Props.get('papersize'),
259         Props.get('language'), Props.get('pagenumber'), linewidth, textheight,
260         linewidth, horizontalMarginArg, textheight, verticalMarginArg,
261         Props.get('header') )
262         
263         pathcomp = os.path.splitext(file)
264         this.__base = pathcomp[0]
265         this.__outfile = '%s.%d%s' % (pathcomp[0], os.getpid(), pathcomp[1])
266         try:
267             this.__fd = open(this.__outfile,"w")
268         except:
269             sys.exit('ExitNoWrite', this.__outfile)
270         this.write(top)
271         this.__mudelaDefs('')
272         this.write("""\
273 \\cmrtwenty% ugh
274 \\makelilytitle
275 """) 
276
277     #
278     # next
279     #
280     def next(this):
281         """
282         Write LaTeX subheader information to support more than one
283         score in a document.  Lastly send current title information to
284         output.
285
286         input:  None
287         output: None
288         error:  None
289         """
290
291         this.write("""\
292 \\def\\theopus{}%
293 \\def\\thepiece{}%
294 \\def\\mudelaopus{}%
295 \\def\\mudelapiece{}%
296 """)
297         this.__mudelaDefs("\\def")
298         this.write("""\
299 \\def\\theopus{\\mudelaopus}% ugh
300 \\def\\thepiece{\\mudelapiece}%
301 \\makelilypiecetitle
302 """)
303
304
305     #
306     # end
307     #
308     def end(this):
309         """
310         Close output file and run latex on it.
311
312         input:  None
313         output: None
314         error:  ExitBadLatex Exception
315         """
316
317         outfile=this.__base + '.dvi'
318         if Props.get('output') != '':
319             outfile = os.path.join(Props.get('output'), outfile )
320             
321         this.write("""\
322 \\vfill\\hfill{\\LilyIdString}
323 \\end{document}
324 """)
325         this.__fd.close()
326         if ( os.name == 'posix' ):
327             stat = os.system('latex \'\\nonstopmode \\input %s\'' %
328                              (this.__outfile))
329         else: # Windows shells don't eat the single quotes
330             stat = os.system('latex \\nonstopmode \\input %s' %
331                              (this.__outfile))
332         if stat:
333             sys.exit('ExitBadLatex')
334         if os.path.isfile(outfile):
335             os.remove(outfile)
336         os.rename(this.__base + '.' + str(os.getpid()) + '.dvi', outfile)
337         sys.stderr.write( '\n' + program_id() + ': dvi file name is %s\n\n'
338                    % (outfile))
339
340         if Props.get('postscript'):
341             psoutfile=this.__base + '.ps'
342             if Props.get('output') != '':
343                 psoutfile = os.path.join(Props.get('output'), psoutfile )
344             stat = os.system('dvips -o %s %s' % (psoutfile,outfile))
345             if stat:
346                 sys.exit('ExitBadPostscript')
347             
348
349 \f
350
351 class Properties:
352     """
353     This class handles all ly2dvi.py property manipulation
354
355     Public methods:
356     
357     __init__()  Constructor
358     set<property> methods
359     """
360
361     def __init__(this):
362
363         #
364         # Following is the order of priority for property assignment.  The
365         # list is organized from lowest to highest priority.  Each
366         # assignment is overridden by the next requester in the list.
367         #
368         # Requester     Description
369         # ---------     -----------
370         # init          Initial default values
371         # file          The values found in the lilypond generated TeX files
372         # environment   Envrionment variables LILYINCLUDE, LILYPONDPREFIX
373         # rcfile        $LILYPONDPREFIX/.lilyrc
374         # rcfile        $HOME/.lilyrc
375         # rcfile        ./.lilyrc
376         # commandline   command line arguments
377         # 
378         this.__overrideTable = {
379             'init'        : 0,
380             'file'        : 1,
381             'environment' : 2,
382             'rcfile'      : 3,
383             'commandline' : 4,
384             'program'     : 5
385             }
386
387         this.__roverrideTable = {} # reverse lookup used for debug
388         for i in this.__overrideTable.items():
389             this.__roverrideTable[i[1]]=i[0]
390         
391         this.__data = {
392             'pagewidth'    :  [597, this.__overrideTable['init']],
393             'pageheight'   :  [845, this.__overrideTable['init']],
394             'papersize'    :  ['a4paper', this.__overrideTable['init']],
395             'textheight'   :  [0, this.__overrideTable['init']],
396             'linewidth'    :  [500, this.__overrideTable['init']],
397             'orientation'  :  ['portrait', this.__overrideTable['init']],
398             'language'     :  ['%', this.__overrideTable['init']],
399             'include'      :  [[], this.__overrideTable['init']],
400             'debug'        :  [0, this.__overrideTable['init']],
401             'keeplilypond' :  [0, this.__overrideTable['init']],
402             'keeply2dvi'   :  [0, this.__overrideTable['init']],
403             'pagenumber'   :  ['%', this.__overrideTable['init']],
404             'separate'     :  [0, this.__overrideTable['init']],
405             'output'       :  ['', this.__overrideTable['init']],
406             'header'       :  ['%', this.__overrideTable['init']],
407             'dependencies' :  [0, this.__overrideTable['init']],
408             'root'         :  ['', this.__overrideTable['init']],
409             'tmp'          :  ['d:\tmp', this.__overrideTable['init']],
410             'filename'     :  ['', this.__overrideTable['init']],
411             'titledefs'    :  [[], this.__overrideTable['init']],
412             'titles'       :  [{}, this.__overrideTable['init']],
413             'lilyOutputFiles' :  [[], this.__overrideTable['init']],
414             'postscript'   :  [0, this.__overrideTable['init']],
415             }
416
417         #
418         # Try to set root and HOME first before calling rcfile
419         #
420         if os.environ.has_key('LILYPONDPREFIX'):
421             this.setRoot(os.environ['LILYPONDPREFIX'], 'environment')
422         else:
423             p=os.path.split(sys.argv[0])
424             p=os.path.split(p[0])
425             # bit silly. for ly2dvi, overrules compiled-in datadir...
426             # how to do this better (without running lily, of course?
427             this.setRoot(os.path.join(p[0],'share','lilypond'), 'init')
428
429         if not os.environ.has_key('HOME'):
430             if os.environ.has_key('HOMEDRIVE') and \
431                  os.environ.has_key('HOMEPATH'):
432                 os.environ['HOME'] = os.environ['HOMEDRIVE'] + \
433                                      os.environ['HOMEPATH']
434             else:
435                 os.environ['HOME'] = os.curdir
436
437         this.rcfile() # Read initialization file(s)
438
439         if os.environ.has_key('LILYINCLUDE'):
440             tmp=this.get('include')
441             for s in string.split(os.environ['LILYINCLUDE'],os.pathsep):
442                 tmp.append(s)
443             this.__set('include', tmp, 'environment')    
444
445
446         t=''
447         if os.environ.has_key ('TEXINPUTS'):
448                 t = os.environ['TEXINPUTS'] + os.pathsep
449         os.environ['TEXINPUTS'] = t + \
450         os.path.join(this.get('root'), 'tex' ) + \
451         os.pathsep + os.path.join(this.get('root'), 'ps' )
452
453         t=''
454         if os.environ.has_key ('MFINPUTS'):
455                 t = os.environ['MFINPUTS'] + os.pathsep
456         os.environ['MFINPUTS'] = t + os.path.join(this.get('root'), 'mf' )
457
458         if os.environ.has_key('TMP'):
459             this.__set('tmp',os.environ['TMP'],'environment')
460
461         
462         fd=this.get_texfile_path ('titledefs.tex')
463         mudefs=[]    
464
465         for line in fd.readlines():
466             m=re.match('\\\\newcommand\*{\\\\mudela([\w]+)}',line)
467             if m:
468                 mudefs.append(m.group(1))
469         fd.close
470         this.__set('titledefs', mudefs, 'init')
471
472     #
473     # __set
474     #
475     def __set(this,var,value,requester):
476         """
477         All of the set methods call this to set a property.  If the value
478         was last set by a requestor of lesser priority the new value is
479         assigned, else the old value has priority and is unchanged.
480         """
481
482         if this.__overrideTable[requester] < this.__data[var][1]:
483             return 0
484         else:
485             this.__data[var] = [value, this.__overrideTable[requester]]
486
487     #
488     # get
489     #
490     def get(this,var):
491         """
492         All of the get methods call this to get a property value.  List
493         variable types are return by value to facilitate an append operation.
494         """
495
496         if var == 'include' or var == 'lilyOutputFiles':
497             return this.__data[var][0][0:]  # return a copy not a ref
498         else:
499             return this.__data[var][0]
500
501     #
502     # get_texfile_path
503     #
504     def get_texfile_path (this, var):
505         """
506         locate and open titledefs.tex file
507         """
508
509         if os.name == 'nt':
510             path = os.path.join(this.get('root'), 'tex', var)
511         else:
512             path =''
513             cmd =('kpsewhich tex %s %s' % (var,errorlog))
514             pipe = os.popen (cmd, 'r')
515             path = pipe.readline ()[:-1] # chop off \n
516             return_status =  pipe.close()
517             if return_status and not path:
518                 path = os.path.join(this.get('root'), 'tex', var)
519         fd = open(path, 'r')
520         return fd
521
522
523     #
524     # Read rc file
525     #
526     def rcfile(this):
527         """
528         Read initialization file(s)
529         """
530         varTable = [
531             #   name              set method
532             #   ----              ----------
533             ( 'DEBUG',          this.setDebug ),
534             ( 'DEPENDENCIES',   this.setDependencies ),
535             ( 'KEEPLILYPOND',   this.setKeeplilypond ),
536             ( 'KEEPLY2DVI',     this.setKeeply2dvi ),
537             ( 'LANGUAGE',       this.setLanguage ),
538             ( 'LATEXHF',        this.setHeader ),
539             ( 'LILYINCLUDE',    this.setInclude ),
540             ( 'LILYPONDPREFIX', this.setRoot ),
541             ( 'NONUMBER',       this.setNonumber ),
542             ( 'ORIENTATION',    this.setOrientation ),
543             ( 'OUTPUTDIR',      this.setOutput ),
544             ( 'PAPERSIZE',      this.setPaperZize ),
545             ( 'PHEIGHT',        this.setTextHeight ),
546             ( 'POSTSCRIPT',     this.setPostscript ),
547             ( 'PWIDTH',         this.setLineWidth ),
548             ( 'SEPARATE',       this.setSeparate ),
549             ( 'TMP',            this.setTmp ),
550             ]
551
552         if ( os.name == 'posix' ):
553             dotFilename='.lilyrc'
554         else: # Windows apps like edit choke on .lilyrc
555             dotFilename='_lilyrc'
556
557         for d in [os.path.join(this.get('root'),'ly'), \
558                   os.environ['HOME'], os.curdir ]:
559             file=os.path.join(d,dotFilename)
560             try:
561                 fd = open( file, 'r' )
562             except:
563                 continue
564             
565             for line in fd.readlines():
566                 if re.match('#.*',line):
567                     continue
568                 m=re.search('([\w]+)=(.*)',line)
569                 if m:
570                     for var in varTable:
571                         if m.group(1) == var[0]:
572                             var[1](m.group(2),'rcfile')
573                             break
574             fd.close
575
576     #
577     # setPaperZize
578     #
579     def setPaperZize(this,size,requester):
580         """
581         Set paper size properties
582         """
583
584         paperTable = [
585             # regex          width    height      name
586             # -----          -----    ------      ----
587             ( 'a0.*',        2389,    3381,    'a0paper' ),
588             ( 'a1$|a1p.*',   1690,    2389,    'a1paper' ),
589             ( 'a2.*',        1194,    1690,    'a2paper' ),
590             ( 'a3.*',        845,     1194,    'a3paper' ),
591             ( 'a4.*',        597,     845,     'a4paper' ),
592             ( 'a5.*',        423,     597,     'a5paper' ),
593             ( 'a6.*',        298,     423,     'a6paper' ),
594             ( 'a7.*',        211,     298,     'a7paper' ),
595             ( 'a8.*',        305,     211,     'a8paper' ),
596             ( 'a9.*',        105,     305,     'a9paper' ),
597             ( 'a10.*',       74,      105,     'a10paper' ),
598             ( 'b0.*',        2847,    4023,    'b0paper' ),
599             ( 'b1.*',        2012,    2847,    'b1paper' ),
600             ( 'b2.*',        1423,    2012,    'b2paper' ),
601             ( 'b3.*',        1006,    1423,    'b3paper' ),
602             ( 'b4.*',        712,     1006,    'b4paper' ),
603             ( 'b5.*',        503,     712,     'b5paper' ),
604             ( 'archA$',      650,     867,     'archApaper' ),
605             ( 'archB$',      867,     1301,    'archBpaper' ),
606             ( 'archC$',      1301,    1734,    'archCpaper' ),
607             ( 'archD$',      1734,    2602,    'archDpaper' ),
608             ( 'archE$',      2602,    3469,    'archEpaper' ),
609             ( 'flsa$|flse$', 614,     940,     'flsapaper' ),
610             ( 'halfletter$', 397,     614,     'halfletterpaper' ),
611             ( 'ledger$',     1229,    795,     'ledgerpaper' ),
612             ( 'legal$',      614,     1012,    'legalpaper' ),
613             ( 'letter$',     614,     795,     'letterpaper' ),
614             ( 'note$',       542,     723,     'notepaper' )
615             ]
616
617         found=0
618         for paper in paperTable:
619             if re.match(paper[0],size):
620                 found=1
621                 this.__set('pagewidth',paper[1],requester)
622                 this.__set('pageheight',paper[2],requester)
623                 this.__set('papersize',paper[3],requester)
624                 break
625
626         if not found:
627             sys.exit('ExitBadPaper',size)
628
629     #
630     # setTextHeight
631     #
632     def setTextHeight(this,size,requester):
633         """
634         Set textheight property
635         """
636
637         m=re.match('([0-9][.0-9]*)(cm|mm|pt|$)',size)
638         if m:
639             if m.group(2) == 'cm':
640                 this.__set('textheight',\
641                            float(m.group(1)) * 72.27/2.54, requester )
642             elif m.group(2) == 'mm':
643                 this.__set('textheight',\
644                            float(m.group(1)) * 72.27/25.4, requester )
645             elif m.group(2) == 'pt':
646                 this.__set('textheight', float(m.group(1)), requester )
647             elif m.group(2) == '':
648                 this.__set('textheight', float(m.group(1)), requester )
649             else:
650                 sys.exit('ExitBadHeight', m.group(2))
651         else:           
652             sys.exit('ExitBadHeight', size)
653
654     #
655     # setLineWidth
656     #
657     def setLineWidth(this,size,requester):
658         """
659         Set linewidth propery
660         """
661
662         m=re.match('([0-9][.0-9]*)(cm|mm|pt|$)',size)
663         if m:
664             if m.group(2) == 'cm':
665                 this.__set('linewidth', \
666                 float(m.group(1)) * 72.27/2.54, requester )
667             elif m.group(2) == 'mm':
668                 this.__set('linewidth', \
669                 float(m.group(1)) * 72.27/25.4, requester )
670             elif m.group(2) == 'pt':
671                 this.__set('linewidth', float(m.group(1)), requester )
672             elif m.group(2) == '':
673                 this.__set('linewidth', float(m.group(1)), requester )
674             else:
675                 sys.exit('ExitBadWidth', m.group(2))
676         else:           
677             sys.stderr.write ('ly2dvi: warning: ignoring linewidth: ' + size + '\n')
678
679     #
680     # setOrientation
681     #
682     def setOrientation(this,orient,requester):
683         """
684         Set orientation property
685         """
686
687         if orient == 'landscape' or orient == 'portrait':
688             this.__set('orientation', orient, requester )
689         else:
690             sys.exit('ExitBadOrient', orient)
691
692     #
693     # setLanguage
694     #
695     def setLanguage(this,lang,requester):
696         """
697         Set language property
698         """
699
700         this.__set('language', '\\usepackage[%s]{babel}' % (lang), requester )
701
702     #
703     # setInclude
704     #
705     def setInclude(this,inc, requester):
706         """
707         Append an include path
708         """
709
710         tmp = this.get('include')
711         tmp.append(inc)
712         this.__set('include', tmp, requester )
713
714     #
715     # setDebug
716     #
717     def setDebug(this,value,requester):
718         """
719         Set or Clear debug flag
720         """
721
722         if int(value) == 1:
723             this.__set('debug',1,requester)
724         else:
725             this.__set('debug',0,requester)
726
727     #
728     # setKeeplilypond
729     #
730     def setKeeplilypond(this, value, requester):        
731         """
732         Set or Clear keeplilypond flag
733         """
734
735         if int(value) == 1:
736             this.__set('keeplilypond',1,requester)
737         else:
738             this.__set('keeplilypond',0,requester)
739
740     #
741     # setKeeply2dvi
742     #
743     def setKeeply2dvi(this, value, requester):  
744         """
745         Set or Clear keeply2dvi flag
746         """
747
748         if int(value) == 1:
749             this.__set('keeply2dvi',1,requester)
750         else:
751             this.__set('keeply2dvi',0,requester)
752
753     #
754     # setNonumber 
755     #
756     def setNonumber(this, value, requester):    
757         """
758         Set nonumber flag
759         """
760
761         if int(value) == 1:
762             this.__set('pagenumber',1,requester)
763         else:
764             this.__set('pagenumber',0,requester)
765
766     #
767     # setSeparate
768     #
769     def setSeparate(this, value, requester):    
770         """
771         Set or Clear separate flag
772         """
773
774         if int(value) == 1:
775             this.__set('separate',1,requester)
776         else:
777             this.__set('separate',0,requester)
778
779     #
780     # Set output directory name
781     #
782     def setOutput(this,out,requester):
783         this.__set('output',out,requester)
784
785     #
786     # Set latex header name
787     #
788     def setHeader(this,head, requester):
789         this.__set('header',head,requester)
790
791     #
792     # Set or Clear Dependencies flag to generate makefile dependencies
793     #
794     def setDependencies(this, requester):       
795         """
796         Set or Clear dependencies flag
797         """
798
799         if int(value) == 1:
800             this.__set('dependencies',1,requester)
801         else:
802             this.__set('dependencies',0,requester)
803
804     #
805     # Set tmp directory
806     #
807     def setTmp(this,dir, requester):    
808         this.__set('tmp',dir,requester)
809
810     #
811     # Set mudela source file name
812     #
813     def setFilename(this,file, requester):      
814         this.__set('filename',file,requester)
815
816     #
817     # Set title commands
818     #
819     def setTitles(this,titles, requester):      
820         this.__set('titles',titles,requester)
821
822     #
823     # Set title commands
824     #
825     def addLilyOutputFiles(this,filelist,requester):
826         """
827         Add a to the lily output list
828         """
829
830         tmp = this.get('lilyOutputFiles')
831         tmp = tmp + filelist
832         this.__set('lilyOutputFiles',tmp,requester)
833
834     #
835     # Set/Clear postscript flag
836     #
837     def setPostscript(this,value,requester):
838         """
839         Set postscript flag
840         """
841
842         if int(value) == 1:
843             this.__set('postscript',1,requester)
844         else:
845             this.__set('postscript',0,requester)
846
847     #
848     # Set root
849     #
850     def setRoot(this,path, requester):  
851         """
852         Set lilypond root directory
853         """
854
855         os.environ['LILYPONDPREFIX'] = path
856         this.__set('root',path,requester)
857
858     #
859     # printProps
860     #
861     def printProps(this):
862         """
863         Print properties
864         """
865         
866         for key in this.__data.keys():
867             print "%s <%s>:<%s>" % (key,this.get(key),
868                                     this.__roverrideTable[this.__data[key][1]])
869
870 \f
871
872 #
873 # Misc functions
874 #
875
876 def getLilyopts():
877     inc = ''    
878     if len(Props.get('include')) > 0: 
879         inc = string.join (map (lambda x: '-I "%s"' % x, Props.get('include')))
880     else:
881
882         if Props.get('dependencies'):
883             dep=' -d'
884         else:
885             dep=''
886         return inc + dep
887     return inc
888
889 def writeLilylog(contents):
890     if Props.get('keeplilypond'):
891         file='lilylog.' + str(os.getpid())
892         output = Props.get('output')
893         if output != '':
894             file = os.path.join( output, file )
895         try:
896             fd = open( file, 'w' )
897         except:
898             sys.exit('ExitNoWrite', file)
899         fd.write(contents)
900         fd.close()
901
902 def getTeXFile(contents):
903     texfiles=[]
904     for line in string.split(contents,'\n'):
905         m = re.search('^Paper output to (.+)\.\.\.', line)
906         if m:
907             texfiles.append(m.group(1))
908
909     if texfiles == []:
910         sys.exit('ExitNoTeXName')
911     else:
912         return texfiles
913
914 def program_id ():
915     return name + ' ' + version;
916
917
918 def mailaddress():
919     try:
920         return os.environ['MAILADDRESS']
921     except KeyError:
922         return '(address unknown)'
923
924
925 def identify ():
926     sys.stderr.write (program_id () + '\n')
927
928 def help ():
929     sys.stderr.write (
930         'Generate dvi file from mudela or lilypond output\n'
931         'Usage: ' + name + ' [OPTION]... [FILE]...\n'
932         '\n'
933         'Options:\n'
934         '  -D,--debug           increase verbosity\n'
935         '  -F,--headers=        name of additional LaTeX headers file\n'
936         '  -H,--Height=         set paper height (points) (see manual page)\n'
937         '  -I,--include=DIR     add DIR to LilyPond\'s search path\n'
938         '  -K,--keeplilypond    keep lilypond output files\n'
939         '  -L,--landscape       set landscape orientation\n'
940         '  -N,--nonumber        switch off page numbering\n'
941         '  -O,--orientation=    set orientation (obsolete - use -L instead)\n'
942         '  -P,--postscript      generate postscript file\n'
943         '  -W,--Width=          set paper width (points) (see manual page)\n'
944         '  -d,--dependencies    tell lilypond make a dependencies file\n'
945         '  -h,--help            this help text\n'
946         '  -k,--keeply2dvi      keep ly2dvi output files\n'
947         '  -l,--language=       give LaTeX language (babel)\n'
948         '  -o,--output=         set output directory\n'
949         '  -p,--papersize=      give LaTeX papersize (eg. a4)\n'
950         '  -s,--separate        run all files separately through LaTeX\n'
951         '\n'
952         'files may be (a mix of) input to or output from lilypond(1)\n'
953         )
954
955 \f
956
957 #
958 # main
959 #
960
961 def main():
962     """Generate dvi files from lilypond source/output"""
963
964     infile = Input()
965     outfile = TeXOutput()
966     texInputFiles=[]
967
968     (options, files) = getopt.getopt (sys.argv[1:],
969                                       'DF:H:I:KLNPW:dhkl:o:p:s',
970                                       ['debug', 'headers=', 'Height=',
971                                        'include=', 'keeplilypond', 'landscape',
972                                        'nonumber', 'Width=', 'dependencies',
973                                        'help', 'keeply2dvi', 'language=',
974                                        'output=', 'papersize=', 'separate',
975                                        'postscript'])
976     for opt in options:
977         o = opt[0]
978         a = opt[1]
979         if o == '--debug' or o == '-D':
980             Props.setDebug(1,'commandline')
981         elif o == '--headers' or o == '-F':
982             Props.setHeader(a,'commandline')
983         elif o == '--include' or o == '-I':
984             Props.setInclude(a,'commandline')
985         elif o == '--Height' or o == '-H':
986             Props.setTextHeight(a,'commandline')
987         elif o == '--keeplilypond' or o == '-K':
988             Props.setKeeplilypond(1,'commandline')
989         elif o == '--landscape' or o == '-L':
990             Props.setOrientation('landscape','commandline')
991         elif o == '--nonumber' or o == '-N':
992             Props.setNonumber('commandline')
993         elif o == '--Width' or o == '-W':
994             Props.setLineWidth(a,'commandline')
995         elif o == '--dependencies' or o == '-d':
996             Props.setDependencies(1,'commandline')
997         elif o == '--help' or o == '-h':
998             help()
999             return 0
1000         elif o == '--keeply2dvi' or o == '-k':
1001             Props.setKeeply2dvi(1,'commandline')
1002         elif o == '--language' or o == '-l':
1003             Props.setLanguage(a,'commandline')
1004         elif o == '--output' or o == '-o':
1005             Props.setOutput(a,'commandline')
1006         elif o == '--papersize' or o == '-p':
1007             Props.setPaperZize(a,'commandline')
1008         elif o == '--separate' or o == '-s':
1009             Props.setSeparate(1,'commandline')
1010         elif o == '--postscript' or o == '-P':
1011             Props.setPostscript(1,'commandline')
1012
1013     if len(files):
1014         for file in files:
1015             infile.open(file)
1016             type = infile.type()
1017             infile.close()
1018             if type == 'source':
1019                 if os.environ.has_key('OS') and \
1020                    os.environ['OS'] == 'Windows_95':
1021                     cmd = 'ash -c "lilypond %s %s 2>&1"' %(getLilyopts(), file)
1022                 else:
1023                     cmd = 'lilypond %s %s 2>&1' % (getLilyopts(), file)
1024                 sys.stderr.write ('executing: %s\n'% cmd)
1025                 
1026                 fd = os.popen(cmd , 'r')
1027                 log = ''
1028                 
1029                 s = fd.readline()
1030                 while len(s) > 0:
1031                         sys.stderr.write (s)
1032                         sys.stderr.flush ()
1033                         log = log + s
1034                         s = fd.readline ()
1035                 if 0:
1036                         s = fd.read (1)
1037                         while len(s) > 0:
1038                                 sys.stderr.write (s)
1039                                 sys.stderr.flush ()
1040                                 s = fd.read (1)                 
1041                         log = log + s
1042                 stat = fd.close()
1043                 if stat:
1044                     sys.exit('ExitBadLily', cmd )
1045                 texFiles=getTeXFile(log)
1046                 writeLilylog(log)
1047                 Props.addLilyOutputFiles(texFiles,'program')
1048                 texInputFiles = texInputFiles + texFiles
1049             else:
1050                 texInputFiles.append(file)
1051
1052         firstfile=1
1053         for file in texInputFiles:
1054             infile.open(file)
1055             infile.setVars() # first pass set variables
1056             infile.close()
1057             if Props.get('debug'):
1058                 Props.printProps()
1059             if firstfile:
1060                 outfile.start(file)
1061             else:
1062                 outfile.next()
1063             outfile.write("""\
1064 \\input{%s}
1065 """ % (file))
1066             if Props.get('separate'):
1067                 outfile.end()
1068             else:
1069                 firstfile=0
1070         if not Props.get('separate'):
1071             outfile.end()
1072     else:
1073         help()
1074         sys.exit('ExitBadArgs','No files specified')
1075
1076 #
1077 # Exit values
1078 #
1079 ExitTable = {
1080     'ExitInterupt'         : ['Ouch!', 1 ],
1081     'ExitBadArgs'          : ['Wrong number of arguments', 2 ],
1082     'ExitNotFound'         : ['File not found', 3 ],
1083     'ExitBadPaper'         : ['Unknown papersize', 4 ],
1084     'ExitBadHeight'        : ['Invalid Height specification', 5 ],
1085     'ExitBadWidth'         : ['Invalid Width specification', 6 ],
1086     'ExitBadOrient'        : ['Invalid Orientation specification', 7 ],
1087     'ExitNoWrite'          : ['Permission denied', 8 ],
1088     'ExitNoTeXName'        : ['hmm, I could not find an output file name', 9 ],
1089     'ExitBadLily'          : ['Lilypond failed', 10 ],
1090     'ExitBadLatex'         : ['Latex failed', 11 ],
1091     'ExitBadPostscript'    : ['Postscript failed', 12 ],
1092     'ExitUnknown'          : ['Unknown Exit Code', 20 ],
1093     }
1094
1095 def cleanup():
1096     lilyfiles = []
1097     tmpfiles = []
1098     if not Props.get('keeplilypond'):
1099         lilyfiles = Props.get('lilyOutputFiles')
1100     if not Props.get('keeply2dvi'):
1101         tmpfiles = glob.glob('*.' + str(os.getpid()) + '.*' )
1102     for file in lilyfiles + tmpfiles:
1103         if os.path.isfile(file):
1104             os.remove(file)
1105
1106
1107 identify()
1108 Props = Properties()
1109
1110 try:
1111     main()
1112
1113 except KeyboardInterrupt:
1114     print ExitTable['ExitInterupt'][0]
1115     cleanup()
1116     sys.exit(ExitTable['ExitInterupt'][1])
1117
1118 except SystemExit, errno:
1119     if ExitTable.has_key(errno.args[0]):
1120         msg = ExitTable[errno.args[0]]
1121     else:
1122         msg = ExitTable['ExitUnknown']
1123     if len(errno.args) > 1:  
1124         sys.stderr.write( '%s: %s: %s\n' % (name, msg[0], errno.args[1]))
1125     else:
1126         sys.stderr.write( '%s %s\n' % (name, msg[0]))
1127     if Props.get('debug'):
1128         Props.printProps()
1129     cleanup()
1130     sys.exit(msg[1])
1131 else:
1132     cleanup()