]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi.py
* Documentation/user/lilypond.tely: Add dir entries for
[lilypond.git] / scripts / ly2dvi.py
1 #!@PYTHON@
2
3 # Run lilypond, latex, dvips.
4 #
5 # This is the third incarnation of ly2dvi.
6 #
7 # Earlier incarnations of ly2dvi were written by
8 # Jeffrey B. Reed<daboys@austin.rr.com> (Python version)
9 # Jan Arne Fagertun <Jan.A.Fagertun@@energy.sintef.no> (Bourne shell script)
10 #
11
12 #
13 # Note: gettext work best if we use ' for docstrings and "
14 #       for gettextable strings.
15 #       --> DO NOT USE """ for docstrings.
16
17 '''
18 TODO:
19
20   * figure out which set of command line options should make ly2dvi:
21
22       na: create tex only?  
23       na: create latex only? 
24       na: create tex and latex
25       default: create dvi only
26       na: create tex, latex and dvi
27       -P: create dvi and ps
28       na: * create ps only
29
30      etc.
31
32      for foo.ly, rename ly2dvi.dir to out-ly2dvi, foo.ly2dvi, foo.dir ?
33      
34   * move versatile taglines, 
35   
36      \header {
37         beginfooter=\mutopiaPD
38         endfooter=\tagline  -> 'lily was here <version>'
39      }
40
41      lilytagline (->lily was here), usertagline, copyright etc.
42
43   * head/header tagline/endfooter
44
45   * dvi from lilypond .tex output?  This is hairy, because we create dvi
46     from lilypond .tex *and* header output.
47
48   * multiple \score blocks?
49   
50 '''
51
52
53 import os
54 import stat
55 import string
56 import re
57 import getopt
58 import sys
59 import shutil
60 import __main__
61 import operator
62 import tempfile
63 import traceback
64
65
66 ################################################################
67 # lilylib.py -- options and stuff
68
69 # source file of the GNU LilyPond music typesetter
70
71 # Handle bug in Python 1.6-2.1
72 #
73 # there are recursion limits for some patterns in Python 1.6 til 2.1. 
74 # fix this by importing pre instead. Fix by Mats.
75
76 # todo: should check Python version first.
77 try:
78         import pre
79         re = pre
80         del pre
81 except ImportError:
82         import re
83
84 # Attempt to fix problems with limited stack size set by Python!
85 # Sets unlimited stack size. Note that the resource module only
86 # is available on UNIX.
87 try:
88        import resource
89        resource.setrlimit (resource.RLIMIT_STACK, (-1, -1))
90 except:
91        pass
92
93 try:
94         import gettext
95         gettext.bindtextdomain ('lilypond', localedir)
96         gettext.textdomain ('lilypond')
97         _ = gettext.gettext
98 except:
99         def _ (s):
100                 return s
101
102 program_version = '@TOPLEVEL_VERSION@'
103 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
104         program_version = '1.5.54'
105
106 def identify ():
107         sys.stdout.write ('%s (GNU LilyPond) %s\n' % (program_name, program_version))
108
109 def warranty ():
110         identify ()
111         sys.stdout.write ('\n')
112         sys.stdout.write (_ ('Copyright (c) %s by' % ' 2001--2002'))
113         sys.stdout.write ('\n')
114         sys.stdout.write ('  Han-Wen Nienhuys')
115         sys.stdout.write ('  Jan Nieuwenhuizen')
116         sys.stdout.write ('\n')
117         sys.stdout.write (_ (r'''
118 Distributed under terms of the GNU General Public License. It comes with
119 NO WARRANTY.'''))
120         sys.stdout.write ('\n')
121
122 def progress (s):
123         errorport.write (s + '\n')
124
125 def warning (s):
126         progress (_ ("warning: ") + s)
127
128 def user_error (s, e=1):
129         errorport.write (program_name + ":" + _ ("error: ") + s + '\n')
130         sys.exit (e)
131         
132 def error (s):
133         '''Report the error S.  Exit by raising an exception. Please
134         do not abuse by trying to catch this error. If you do not want
135         a stack trace, write to the output directly.
136
137         RETURN VALUE
138
139         None
140         
141         '''
142         
143         progress (_ ("error: ") + s)
144         raise _ ("Exiting ... ")
145
146 def getopt_args (opts):
147         '''Construct arguments (LONG, SHORT) for getopt from  list of options.'''
148         short = ''
149         long = []
150         for o in opts:
151                 if o[1]:
152                         short = short + o[1]
153                         if o[0]:
154                                 short = short + ':'
155                 if o[2]:
156                         l = o[2]
157                         if o[0]:
158                                 l = l + '='
159                         long.append (l)
160         return (short, long)
161
162 def option_help_str (o):
163         '''Transform one option description (4-tuple ) into neatly formatted string'''
164         sh = '  '       
165         if o[1]:
166                 sh = '-%s' % o[1]
167
168         sep = ' '
169         if o[1] and o[2]:
170                 sep = ','
171                 
172         long = ''
173         if o[2]:
174                 long= '--%s' % o[2]
175
176         arg = ''
177         if o[0]:
178                 if o[2]:
179                         arg = '='
180                 arg = arg + o[0]
181         return '  ' + sh + sep + long + arg
182
183
184 def options_help_str (opts):
185         '''Convert a list of options into a neatly formatted string'''
186         w = 0
187         strs =[]
188         helps = []
189
190         for o in opts:
191                 s = option_help_str (o)
192                 strs.append ((s, o[3]))
193                 if len (s) > w:
194                         w = len (s)
195
196         str = ''
197         for s in strs:
198                 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0])  + 3), s[1])
199         return str
200
201 def help ():
202         ls = [(_ ("Usage: %s [OPTION]... FILE") % program_name),
203                 ('\n\n'),
204                 (help_summary),
205                 ('\n\n'),
206                 (_ ("Options:")),
207                 ('\n'),
208                 (options_help_str (option_definitions)),
209                 ('\n\n'),
210                 (_ ("Report bugs to %s") % 'bug-lilypond@gnu.org'),
211                 ('\n')]
212         map (sys.stdout.write, ls)
213         
214 def setup_temp ():
215         """
216         Create a temporary directory, and return its name. 
217         """
218         global temp_dir
219         if not keep_temp_dir_p:
220                 temp_dir = tempfile.mktemp (program_name)
221         try:
222                 os.mkdir (temp_dir, 0777)
223         except OSError:
224                 pass
225
226         return temp_dir
227
228
229 def system (cmd, ignore_error = 0, quiet =0):
230         """Run CMD. If IGNORE_ERROR is set, don't complain when CMD returns non zero.
231
232         RETURN VALUE
233
234         Exit status of CMD
235         """
236         
237         if verbose_p:
238                 progress (_ ("Invoking `%s\'") % cmd)
239
240         st = os.system (cmd)
241         if st:
242                 name = re.match ('[ \t]*([^ \t]*)', cmd).group (1)
243                 msg = name + ': ' + _ ("command exited with value %d") % st
244                 if ignore_error:
245                         if not quiet:
246                                 warning (msg + ' ' + _ ("(ignored)") + ' ')
247                 else:
248                         error (msg)
249
250         return st
251
252
253 def cleanup_temp ():
254         if not keep_temp_dir_p:
255                 if verbose_p:
256                         progress (_ ("Cleaning %s...") % temp_dir)
257                 shutil.rmtree (temp_dir)
258
259
260 def strip_extension (f, ext):
261         (p, e) = os.path.splitext (f)
262         if e == ext:
263                 e = ''
264         return p + e
265
266
267 def cp_to_dir (pattern, dir):
268         "Copy files matching re PATTERN from cwd to DIR"
269         # Duh.  Python style portable: cp *.EXT OUTDIR
270         # system ('cp *.%s %s' % (ext, outdir), 1)
271         files = filter (lambda x, p=pattern: re.match (p, x), os.listdir ('.'))
272         map (lambda x, d=dir: shutil.copy2 (x, os.path.join (d, x)), files)
273
274
275 # Python < 1.5.2 compatibility
276 #
277 # On most platforms, this is equivalent to
278 #`normpath(join(os.getcwd()), PATH)'.  *Added in Python version 1.5.2*
279 if os.path.__dict__.has_key ('abspath'):
280         abspath = os.path.abspath
281 else:
282         def abspath (path):
283                 return os.path.normpath (os.path.join (os.getcwd (), path))
284
285 if os.__dict__.has_key ('makedirs'):
286         makedirs = os.makedirs
287 else:
288         def makedirs (dir, mode=0777):
289                 system ('mkdir -p %s' % dir)
290
291
292 def mkdir_p (dir, mode=0777):
293         if not os.path.isdir (dir):
294                 makedirs (dir, mode)
295
296
297 # if set, LILYPONDPREFIX must take prevalence
298 # if datadir is not set, we're doing a build and LILYPONDPREFIX 
299 datadir = '@local_lilypond_datadir@'
300
301 if os.environ.has_key ('LILYPONDPREFIX') :
302         datadir = os.environ['LILYPONDPREFIX']
303 else:
304         datadir = '@local_lilypond_datadir@'
305
306
307 while datadir[-1] == os.sep:
308         datadir= datadir[:-1]
309
310 sys.path.insert (0, os.path.join (datadir, 'python'))
311
312 ################################################################
313 # END Library
314
315
316 program_name = 'ly2dvi'
317
318 original_dir = os.getcwd ()
319 temp_dir = os.path.join (original_dir,  '%s.dir' % program_name)
320 errorport = sys.stderr
321 keep_temp_dir_p = 0
322 verbose_p = 0
323 preview_p = 0
324 preview_resolution = 90
325 pseudo_filter_p = 0
326
327 help_summary = _ ("Generate .dvi with LaTeX for LilyPond")
328
329 option_definitions = [
330         ('', 'd', 'dependencies', _ ("write Makefile dependencies for every input file")),
331         ('', 'h', 'help', _ ("this help")),
332         (_ ("DIR"), 'I', 'include', _ ("add DIR to LilyPond's search path")),
333         ('', 'k', 'keep', _ ("keep all output, and name the directory %s.dir") % program_name),
334         ('', '', 'no-lily', _ ("don't run LilyPond")),
335         ('', 'm', 'no-paper', _ ("produce MIDI output only")),
336         (_ ("FILE"), 'o', 'output', _ ("write ouput to FILE")),
337         (_ ("FILE"), 'f', 'find-pfa', _ ("find pfa fonts used in FILE")),
338         # why capital P?
339         ('', '', 'preview', _("Make a picture of the first system.")),
340         (_ ('RES'), '', 'preview-resolution', _("Set the resolution of the preview to RES.")),
341         ('', 'P', 'postscript', _ ("generate PostScript output")),
342         ('', 'p', 'pdf', _ ("generate PDF output")),    
343         (_ ("KEY=VAL"), 's', 'set', _ ("change global setting KEY to VAL")),
344         ('', 'V', 'verbose', _ ("verbose")),
345         ('', 'v', 'version', _ ("print version number")),
346         ('', 'w', 'warranty', _ ("show warranty and copyright")),
347         ]
348
349 layout_fields = ['dedication', 'title', 'subtitle', 'subsubtitle',
350           'footer', 'head', 'composer', 'arranger', 'instrument',
351           'opus', 'piece', 'metre', 'meter', 'poet', 'texttranslator']
352
353
354 # init to empty; values here take precedence over values in the file
355
356 ## TODO: change name.
357 extra_init = {
358         'language' : [],
359         'latexheaders' : [],
360         'latexpackages' :  ['geometry'],
361         'latexoptions' : [],
362         'papersize' : [],
363         'pagenumber' : [1],
364         'textheight' : [], 
365         'linewidth' : [],
366         'orientation' : [],
367         'unit' : ['pt'],
368 }
369
370 extra_fields = extra_init.keys ()
371 fields = layout_fields + extra_fields
372
373 include_path = ['.']
374 lily_p = 1
375 paper_p = 1
376
377 output_name = ''
378
379 # List of output formats that ly2dvi should create
380 # Most advanced format first.
381 targets = ['DVI', 'LATEX', 'MIDI', 'TEX']
382
383 track_dependencies_p = 0
384 dependency_files = []
385
386
387
388 kpse = os.popen ('kpsexpand \$TEXMF').read()
389 kpse = re.sub('[ \t\n]+$','', kpse)
390 type1_paths = os.popen ('kpsewhich -expand-path=\$T1FONTS').read ()
391
392 environment = {
393         # TODO: * prevent multiple addition.
394         #       * clean TEXINPUTS, MFINPUTS, TFMFONTS,
395         #         as these take prevalence over $TEXMF
396         #         and thus may break tex run?
397         'TEXMF' : "{%s,%s}" % (datadir, kpse) ,
398         'GS_FONTPATH' : type1_paths,
399         'GS_LIB' : datadir + '/ps',
400 }
401
402 # tex needs lots of memory, more than it gets by default on Debian
403 non_path_environment = {
404         'extra_mem_top' : '1000000',
405         'extra_mem_bottom' : '1000000',
406         'pool_size' : '250000',
407 }
408
409 def setup_environment ():
410         # $TEXMF is special, previous value is already taken care of
411         if os.environ.has_key ('TEXMF'):
412                 del os.environ['TEXMF']
413  
414         for key in environment.keys ():
415                 val = environment[key]
416                 if os.environ.has_key (key):
417                         val = os.environ[key] + os.pathsep + val 
418                 os.environ[key] = val
419
420         for key in non_path_environment.keys ():
421                 val = non_path_environment[key]
422                 os.environ[key] = val
423
424 #what a name.
425 def set_setting (dict, key, val):
426         try:
427                 val = string.atoi (val)
428         except ValueError:
429                 #warning (_ ("invalid value: %s") % `val`)
430                 pass
431
432         if type(val) == type ('hoi'):
433                 try:
434                         val = string.atof (val)
435                 except ValueError:
436                         #warning (_ ("invalid value: %s") % `val`)
437                         pass
438
439         try:
440                 dict[key].append (val)
441         except KeyError:
442                 warning (_ ("no such setting: %s") % `key`)
443                 dict[key] = [val]
444
445
446 def print_environment ():
447         for (k,v) in os.environ.items ():
448                 sys.stderr.write ("%s=\"%s\"\n" % (k,v)) 
449
450 def quiet_system (cmd, name, ignore_error = 0):
451         if not verbose_p:
452                 progress ( _("Running %s...") % name)
453                 cmd = cmd + ' 1> /dev/null 2> /dev/null'
454         elif pseudo_filter_p:
455                 cmd = cmd + ' 1> /dev/null'
456
457         return system (cmd, ignore_error, quiet = 1)
458
459
460 def run_lilypond (files, dep_prefix):
461
462         opts = ''
463         opts = opts + ' ' + string.join (map (lambda x : '-I ' + x,
464                                               include_path))
465         if pseudo_filter_p:
466                 opts = opts + ' --output=lelie'
467         if paper_p:
468                 opts = opts + ' ' + string.join (map (lambda x : '-H ' + x,
469                                                       fields))
470         else:
471                 opts = opts + ' --no-paper'
472                 
473         if track_dependencies_p:
474                 opts = opts + " --dependencies"
475                 if dep_prefix:
476                         opts = opts + ' --dep-prefix=%s' % dep_prefix
477
478         fs = string.join (files)
479
480         if not verbose_p:
481                 # cmd = cmd + ' 1> /dev/null 2> /dev/null'
482                 progress ( _("Running %s...") % 'LilyPond')
483         else:
484                 opts = opts + ' --verbose'
485
486                 # for better debugging!
487                 print_environment ()
488
489         cmd = 'lilypond %s %s ' % (opts, fs)
490         if  verbose_p:
491                 progress ("Invoking `%s'"% cmd)
492         status = os.system (cmd)
493
494         signal = 0x0f & status
495         exit_status = status >> 8
496
497         # 2 == user interrupt.
498         if signal and  signal <> 2:
499                 error("\n\nLilyPond crashed (signal %d). Please submit a bugreport to bug-lilypond@gnu.org\n" % signal)
500
501         if status:
502                 error ("\n\nLilyPond failed on the input file. (exit status %d)\n" % exit_status)
503                 
504
505 def analyse_lilypond_output (filename, extra):
506         
507         # urg
508         '''Grep FILENAME for interesting stuff, and
509         put relevant info into EXTRA.'''
510         filename = filename+'.tex'
511         progress (_ ("Analyzing %s...") % filename)
512         s = open (filename).read ()
513
514         # search only the first 10k
515         s = s[:10240]
516         for x in extra_fields:
517                 m = re.search (r'\\def\\lilypondpaper%s{([^}]*)}'%x, s)
518                 if m:
519                         set_setting (extra, x, m.group (1))
520
521 def find_tex_files_for_base (base, extra):
522
523         """
524         Find the \header fields dumped from BASE.
525         """
526         
527         headerfiles = {}
528         for f in layout_fields:
529                 if os.path.exists (base + '.' + f):
530                         headerfiles[f] = base+'.'+f
531
532         if os.path.exists (base  +'.dep'):
533                 dependency_files.append (base + '.dep')
534
535         for f in extra_fields:
536                 if os.path.exists (base + '.' + f):
537                         extra[f].append (open (base + '.' + f).read ())
538         
539         return (base  +'.tex',headerfiles)
540          
541
542 def find_tex_files (files, extra):
543         """
544         Find all .tex files whose prefixes start with some name in FILES. 
545
546         """
547         
548         tfiles = []
549         
550         for f in files:
551                 x = 0
552                 while 1:
553                         fname = os.path.basename (f)
554                         fname = strip_extension (fname, '.ly')
555                         if x:
556                                 fname = fname + '-%d' % x
557
558                         if os.path.exists (fname + '.tex'):
559                                 tfiles.append (find_tex_files_for_base (fname, extra))
560                                 analyse_lilypond_output (fname, extra)
561                         else:
562                                 break
563
564                         x = x + 1
565         if not x:
566                 fstr = string.join (files, ', ')
567                 warning (_ ("no LilyPond output found for %s") % fstr)
568         return tfiles
569
570 def one_latex_definition (defn, first):
571         s = '\n'
572         for (k,v) in defn[1].items ():
573                 val = open (v).read ()
574                 if (string.strip (val)):
575                         s = s + r'''\def\lilypond%s{%s}''' % (k, val)
576                 else:
577                         s = s + r'''\let\lilypond%s\relax''' % k
578                 s = s + '\n'
579
580         if first:
581                 s = s + '\\def\\mustmakelilypondtitle{}\n'
582         else:
583                 s = s + '\\def\\mustmakelilypondpiecetitle{}\n'
584                 
585         s = s + '\\input %s\n' % defn[0] # The final \n seems important here. It ensures that the footers and taglines end up on the right page.
586         return s
587
588
589 ly_paper_to_latexpaper =  {
590         'a4' : 'a4paper',
591         'letter' : 'letterpaper', 
592 }
593
594 #TODO: should set textheight (enlarge) depending on papersize. 
595 def global_latex_preamble (extra):
596         '''construct preamble from EXTRA,'''
597         s = ""
598         s = s + '% generation tag\n'
599
600         options = ''
601
602
603         if extra['papersize']:
604                 try:
605                         options = ly_paper_to_latexpaper[extra['papersize'][0]]
606                 except KeyError:
607                         warning (_ ("invalid value: %s") % `extra['papersize'][0]`)
608                         pass
609
610         if extra['latexoptions']:
611                 options = options + ',' + extra['latexoptions'][-1]
612
613         s = s + '\\documentclass[%s]{article}\n' % options
614
615         if extra['language']:
616                 s = s + r'\usepackage[%s]{babel}' % extra['language'][-1] + '\n'
617
618
619         s = s + '\\usepackage{%s}\n' \
620                 % string.join (extra['latexpackages'], ',')
621
622         if extra['latexheaders']:
623                 s = s + '\\include{%s}\n' \
624                         % string.join (extra['latexheaders'], '}\n\\include{')
625
626         unit = extra['unit'][-1]
627
628         textheight = ''
629         if extra['textheight']:
630                 textheight = ',textheight=%f%s' % (extra['textheight'][0], unit)
631
632         orientation = 'portrait'
633         if extra['orientation']:
634                 orientation = extra['orientation'][0]
635
636         # set sane geometry width (a4-width) for linewidth = -1.
637         maxlw = max (extra['linewidth'] + [-1])
638         if maxlw < 0:
639                 # who the hell is 597 ?
640                 linewidth = '597pt'
641         else:
642                 linewidth = '%d%s' % (maxlw, unit)
643         s = s + '\geometry{width=%s%s,headheight=2mm,footskip=2mm,%s}\n' % (linewidth, textheight, orientation)
644
645         if extra['latexoptions']:
646                 s = s + '\geometry{twosideshift=4mm}\n'
647
648         s = s + r'''
649 \usepackage[latin1]{inputenc}
650 \input{titledefs}
651 '''
652         
653         if extra['pagenumber'] and extra['pagenumber'][-1] and extra['pagenumber'][-1] != 'no':
654                 s = s + '\setcounter{page}{%d}\n' % (extra['pagenumber'][-1])
655                 s = s + '\\pagestyle{plain}\n'
656         else:
657                 s = s + '\\pagestyle{empty}\n'
658
659
660         return s
661
662         
663 def global_latex_definition (tfiles, extra):
664         '''construct preamble from EXTRA, dump Latex stuff for each
665 lily output file in TFILES after that, and return the Latex file constructed.  '''
666
667         
668         s = global_latex_preamble (extra) + '\\begin{document}\n'
669         s = s + '\\thispagestyle{firstpage}\n'
670
671         first = 1
672         for t in tfiles:
673                 s = s + one_latex_definition (t, first)
674                 first = 0
675
676
677         s = s + '\\thispagestyle{lastpage}\n'
678         s = s + '\\end{document}'
679
680         return s
681
682 def run_latex (files, outbase, extra):
683
684         """Construct latex file, for FILES and EXTRA, dump it into
685 OUTBASE.latex. Run LaTeX on it.
686
687 RETURN VALUE
688
689 None
690         """
691
692         latex_fn = outbase + '.latex'
693         
694         wfs = find_tex_files (files, extra)
695         s = global_latex_definition (wfs, extra)
696
697         f = open (latex_fn, 'w')
698         f.write (s)
699         f.close ()
700
701         cmd = 'latex \\\\nonstopmode \\\\input %s' % latex_fn
702         status = quiet_system (cmd, 'LaTeX', ignore_error = 1)
703
704         signal = 0xf & status
705         exit_stat = status >> 8
706
707         if exit_stat:
708                 logstr = open (outbase + '.log').read()
709                 m = re.search ("\n!", logstr)
710                 start = m.start (0)
711                 logstr = logstr[start:start+200]
712                 
713                 sys.stderr.write(_("""LaTeX failed on the output file.
714 The error log is as follows:
715 %s...\n""" % logstr))
716                 raise 'LaTeX error'
717         
718         if preview_p:
719                 # make a preview by rendering only the 1st line.
720                 preview_fn = outbase + '.preview.tex'
721                 f = open(preview_fn, 'w')
722                 f.write (r'''
723 %s
724 \input lilyponddefs
725 \pagestyle{empty}
726 \begin{document}
727 \def\interscoreline{\endinput}
728 \input %s
729 \end{document}
730 ''' % (global_latex_preamble (extra), outbase))
731
732                 f.close()
733                 cmd = 'latex \\\\nonstopmode \\\\input %s' % preview_fn
734                 quiet_system (cmd, "LaTeX for preview")
735         
736
737 def run_dvips (outbase, extra):
738
739
740         """Run dvips using the correct options taken from EXTRA,
741 leaving a PS file in OUTBASE.ps
742
743 RETURN VALUE
744
745 None.
746 """
747         opts = ''
748         if extra['papersize']:
749                 opts = opts + ' -t%s' % extra['papersize'][0]
750
751         if extra['orientation'] and extra['orientation'][0] == 'landscape':
752                 opts = opts + ' -tlandscape'
753
754         if 'PDF' in targets:
755                 opts = opts + ' -Ppdf -G0 -u lilypond.map'
756                 
757         cmd = 'dvips %s -o%s %s' % (opts, outbase + '.ps', outbase + '.dvi')
758         quiet_system (cmd, 'dvips')
759
760         if preview_p:
761                 cmd = 'dvips -E -o%s %s' % ( outbase + '.preview.ps', outbase + '.preview.dvi')         
762                 quiet_system (cmd, 'dvips for preview')
763
764         if 'PDF' in targets:
765                 cmd = 'ps2pdf %s.ps %s.pdf' % (outbase , outbase)
766                 quiet_system (cmd, 'ps2pdf')
767                 
768 def get_bbox (filename):
769         # cut & paste 
770         system ('gs -sDEVICE=bbox -q  -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1 ' % (filename, filename))
771
772         box = open (filename + '.bbox').read()
773         m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box)
774         gr = []
775         if m:
776                 gr = map (string.atoi, m.groups ())
777         
778         return gr
779
780 #
781 # cut & paste from lilypond-book.
782 #
783 def make_preview (name, extra):
784         bbox = get_bbox (name + '.preview.ps')
785         margin = 0
786         fo = open (name + '.trans.eps' , 'w')
787         fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
788         fo.close ()
789         
790         x = (2* margin + bbox[2] - bbox[0]) * preview_resolution / 72.
791         y = (2* margin + bbox[3] - bbox[1]) * preview_resolution / 72.
792
793         cmd = r'''gs -g%dx%d -sDEVICE=pgm  -dTextAlphaBits=4 -dGraphicsAlphaBits=4  -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit | pnmtopng > %s'''
794         
795         cmd = cmd % (x, y, preview_resolution, name + '.trans.eps', name + '.preview.ps',name + '.png')
796         quiet_system (cmd, 'gs')
797
798         try:
799                 status = system (cmd)
800         except:
801                 os.unlink (name + '.png')
802                 error ("Removing output file")
803
804
805
806 def generate_dependency_file (depfile, outname):
807         df = open (depfile, 'w')
808         df.write (outname + ':' )
809         
810         for d in dependency_files:
811                 s = open (d).read ()
812                 s = re.sub ('#[^\n]*\n', '', s)
813                 s = re.sub (r'\\\n', ' ', s)
814                 m = re.search ('.*:(.*)\n', s)
815
816                 # ugh. Different targets?
817                 if m:
818                         df.write ( m.group (1)  + ' ' )
819
820         df.write ('\n')
821         df.close ();
822
823 def find_file_in_path (path, name):
824         for d in string.split (path, os.pathsep):
825                 if name in os.listdir (d):
826                         return os.path.join (d, name)
827
828 # Added as functionality to ly2dvi, because ly2dvi may well need to do this
829 # in future too.
830 PS = '%!PS-Adobe'
831 def find_pfa_fonts (name):
832         s = open (name).read ()
833         if s[:len (PS)] != PS:
834                 # no ps header?
835                 errorport.write (_( "error: ") + _ ("not a PostScript file: `%s\'" % name))
836                 errorport.write ('\n')
837                 sys.exit (1)
838         here = 0
839         m = re.match ('.*?/(feta[-a-z0-9]+) +findfont', s[here:], re.DOTALL)
840         pfa = []
841         while m:
842                 here = m.end (1)
843                 pfa.append (m.group (1))
844                 m = re.match ('.*?/(feta[-a-z0-9]+) +findfont', s[here:], re.DOTALL)
845         return pfa
846
847         
848 (sh, long) = getopt_args (option_definitions)
849 try:
850         (options, files) = getopt.getopt(sys.argv[1:], sh, long)
851 except getopt.error, s:
852         errorport.write ('\n')
853         errorport.write (_ ("error: ") + _ ("getopt says: `%s\'" % s))
854         errorport.write ('\n')
855         errorport.write ('\n')
856         help ()
857         sys.exit (2)
858         
859 for opt in options:     
860         o = opt[0]
861         a = opt[1]
862
863         if 0:
864                 pass
865         elif o == '--help' or o == '-h':
866                 help ()
867                 sys.exit (0)
868         elif o == '--find-pfa' or o == '-f':
869                 fonts = map (lambda x: x + '.pfa', find_pfa_fonts (a))
870                 files = map (lambda x:
871                              find_file_in_path (os.environ['GS_FONTPATH'], x),
872                              fonts)
873                 print string.join (files, ' ')
874                 sys.exit (0)
875         elif o == '--include' or o == '-I':
876                 include_path.append (a)
877         elif o == '--postscript' or o == '-P':
878                 targets.append ('PS')
879         elif o == '--pdf' or o == '-p':
880                 targets.append ('PS')
881                 targets.append ('PDF')
882         elif o == '--keep' or o == '-k':
883                 keep_temp_dir_p = 1
884         elif o == '--no-lily':
885                 lily_p = 0
886         elif o == '--preview':
887                 preview_p = 1
888                 targets.append ('PNG')
889         elif o == '--preview-resolution':
890                 preview_resolution = string.atoi (a)
891         elif o == '--no-paper' or o == '-m':
892                 targets = ['MIDI'] 
893                 paper_p = 0
894         elif o == '--output' or o == '-o':
895                 output_name = a
896         elif o == '--set' or o == '-s':
897                 ss = string.split (a, '=')
898                 set_setting (extra_init, ss[0], ss[1])
899         elif o == '--dependencies' or o == '-d':
900                 track_dependencies_p = 1
901         elif o == '--verbose' or o == '-V':
902                 verbose_p = 1
903         elif o == '--version' or o == '-v':
904                 identify ()
905                 sys.exit (0)
906         elif o == '--warranty' or o == '-w':
907                 status = system ('lilypond -w', ignore_error = 1)
908                 if status:
909                         warranty ()
910
911                 sys.exit (0)
912
913 print 'hallo3'
914 # Don't convert input files to abspath, rather prepend '.' to include
915 # path.
916 include_path.insert (0, '.')
917
918 # As a neat trick, add directory part of first input file
919 # to include path.  That way you can do without the clumsy -I in:
920
921 #    ly2dvi -I foe/bar/baz foo/bar/baz/baz.ly
922 if files and files[0] != '-' and os.path.dirname (files[0]) != '.':
923         include_path.append (os.path.dirname (files[0]))
924         
925 include_path = map (abspath, include_path)
926
927 if files and (files[0] == '-' or output_name == '-'):
928         if len (files) == 1:
929                 pseudo_filter_p = 1
930                 output_name = 'lelie'
931                 if verbose_p:
932                         progress (_ ("pseudo filter"))
933         else:
934                 help ()
935                 user_error (_ ("pseudo filter only for single input file."), 2)
936                 
937         
938 original_output = output_name
939
940 if files:
941         
942         # Ugh, maybe make a setup () function
943         files = map (lambda x: strip_extension (x, '.ly'), files)
944
945         # hmmm. Wish I'd 've written comments when I wrote this.
946         # now it looks complicated.
947         
948         (outdir, outbase) = ('','')
949         if not output_name:
950                 outbase = os.path.basename (files[0])
951                 outdir = abspath ('.')
952         elif output_name[-1] == os.sep:
953                 outdir = abspath (output_name)
954                 outbase = os.path.basename (files[0])
955         else:
956                 (outdir, outbase) = os.path.split (abspath (output_name))
957
958         for i in ('.dvi', '.latex', '.ly', '.ps', '.tex'):
959                 output_name = strip_extension (output_name, i)
960                 outbase = strip_extension (outbase, i)
961
962         for i in files[:] + [output_name]:
963                 if string.find (i, ' ') >= 0:
964                         user_error (_ ("filename should not contain spaces: `%s'") % i)
965                         
966         if os.path.dirname (output_name) != '.':
967                 dep_prefix = os.path.dirname (output_name)
968         else:
969                 dep_prefix = 0
970
971         reldir = os.path.dirname (output_name)
972         if outdir != '.' and (track_dependencies_p or targets):
973                 mkdir_p (outdir, 0777)
974
975         setup_environment ()
976         tmpdir = setup_temp ()
977
978         # to be sure, add tmpdir *in front* of inclusion path.
979         #os.environ['TEXINPUTS'] =  tmpdir + ':' + os.environ['TEXINPUTS']
980         os.chdir (tmpdir)
981         
982         if lily_p:
983                 try:
984                         run_lilypond (files, dep_prefix)
985                 except:
986                         # TODO: friendly message about LilyPond setup/failing?
987                         #
988                         # TODO: lilypond should fail with different
989                         # error codes for:
990                         #   - guile setup/startup failure
991                         #   - font setup failure
992                         #   - init.ly setup failure
993                         #   - parse error in .ly
994                         #   - unexpected: assert/core dump
995                         targets = []
996                         traceback.print_exc ()
997
998         # Our LilyPond pseudo filter always outputs to 'lelie'
999         # have subsequent stages and use 'lelie' output.
1000         if pseudo_filter_p:
1001                 files[0] = 'lelie'
1002                 
1003         if 'PNG' in targets and 'PS' not in targets:
1004                 targets.append ('PS')
1005         if 'PS' in targets and 'DVI' not in targets:
1006                 targets.append('DVI')
1007
1008         if 'DVI' in targets:
1009                 try:
1010                         run_latex (files, outbase, extra_init)
1011                         # unless: add --tex, or --latex?
1012                         targets.remove ('TEX')
1013                         targets.remove('LATEX')
1014                 except:
1015                         # TODO: friendly message about TeX/LaTeX setup,
1016                         # trying to run tex/latex by hand
1017                         if 'DVI' in targets:
1018                                 targets.remove ('DVI')
1019                         if 'PS' in targets:
1020                                 targets.remove ('PS')
1021                         traceback.print_exc ()
1022
1023         if 'PS' in targets:
1024                 try:
1025                         run_dvips (outbase, extra_init)
1026                 except: 
1027                         if 'PS' in targets:
1028                                 targets.remove ('PS')
1029                         traceback.print_exc ()
1030
1031         if 'PNG' in  targets:
1032                 make_preview (outbase, extra_init)
1033                 
1034         # add DEP to targets?
1035         if track_dependencies_p:
1036                 depfile = os.path.join (outdir, outbase + '.dep')
1037                 generate_dependency_file (depfile, depfile)
1038                 if os.path.isfile (depfile):
1039                         progress (_ ("dependencies output to `%s'...") %
1040                                   depfile)
1041
1042         if pseudo_filter_p:
1043                 main_target = 0
1044                 for i in 'PDF', 'PS', 'PNG', 'DVI', 'LATEX':
1045                         if i in targets:
1046                                 main_target = i
1047                                 break
1048
1049                 outname = outbase + '.' + string.lower (main_target)
1050                 if os.path.isfile (outname):
1051                         sys.stdout.write (open (outname).read ())
1052                 elif verbose_p:
1053                         warning (_ ("can't find file: `%s'") % outname)
1054                 targets = []
1055                 
1056         # Hmm, if this were a function, we could call it the except: clauses
1057         for i in targets:
1058                 ext = string.lower (i)
1059                 cp_to_dir ('.*\.%s$' % ext, outdir)
1060                 outname = outbase + '.' + string.lower (i)
1061                 abs = os.path.join (outdir, outname)
1062                 if reldir != '.':
1063                         outname = os.path.join (reldir, outname)
1064                 if os.path.isfile (abs):
1065                         progress (_ ("%s output to `%s'...") % (i, outname))
1066                 elif verbose_p:
1067                         warning (_ ("can't find file: `%s'") % outname)
1068                         
1069         os.chdir (original_dir)
1070         cleanup_temp ()
1071         
1072 else:
1073         help ()
1074         user_error (_ ("no files specified on command line."), 2)