release commit
[lilypond.git] / scripts / ly2dvi.py
blobfe65221bb597578a5ab44eff62daf014b330444c
1 #!@PYTHON@
3 # Run lilypond, latex, dvips.
5 # This is the third incarnation of ly2dvi.
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)
13 # Note: gettext work best if we use ' for docstrings and "
14 # for gettextable strings.
15 # --> DO NOT USE """ for docstrings.
17 '''
18 TODO:
20 * figure out which set of command line options should make ly2dvi:
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
30 etc.
32 for foo.ly, rename ly2dvi.dir to out-ly2dvi, foo.ly2dvi, foo.dir ?
34 * move versatile taglines,
36 \header {
37 beginfooter=\mutopiaPD
38 endfooter=\tagline -> 'lily was here <version>'
41 lilytagline (->lily was here), usertagline, copyright etc.
43 * head/header tagline/endfooter
45 * dvi from lilypond .tex output? This is hairy, because we create dvi
46 from lilypond .tex *and* header output.
48 * multiple \score blocks?
50 '''
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
66 ################################################################
67 # lilylib.py -- options and stuff
69 # source file of the GNU LilyPond music typesetter
71 # Handle bug in Python 1.6-2.1
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.
76 # todo: should check Python version first.
77 try:
78 import pre
79 re = pre
80 del pre
81 except ImportError:
82 import re
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
93 localedir = '@localedir@'
94 try:
95 import gettext
96 gettext.bindtextdomain ('lilypond', localedir)
97 gettext.textdomain ('lilypond')
98 _ = gettext.gettext
99 except:
100 def _ (s):
101 return s
103 program_version = '@TOPLEVEL_VERSION@'
104 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
105 program_version = '1.5.54'
107 def identify ():
108 sys.stdout.write ('%s (GNU LilyPond) %s\n' % (program_name, program_version))
110 def warranty ():
111 identify ()
112 sys.stdout.write ('\n')
113 sys.stdout.write (_ ('Copyright (c) %s by' % ' 2001--2003'))
114 sys.stdout.write ('\n')
115 sys.stdout.write (' Han-Wen Nienhuys')
116 sys.stdout.write (' Jan Nieuwenhuizen')
117 sys.stdout.write ('\n\n')
118 sys.stdout.write ('\n')
119 sys.stdout.write (_ ("Distributed under terms of the GNU General Public License. It comes with NO WARRANTY."))
120 sys.stdout.write ('\n')
122 def progress (s):
123 errorport.write (s + '\n')
125 def warning (s):
126 progress (_ ("warning: ") + s)
128 def user_error (s, e=1):
129 errorport.write (program_name + ":" + _ ("error: ") + s + '\n')
130 if (e):
131 sys.exit (e)
133 def error (s):
134 '''Report the error S.
136 If verbose is set, exit by raising an exception. Otherwise,
137 simply sys.exit().
139 Please do not abuse by trying to catch this error. If you do
140 not want a stack trace, write to the output directly.
142 RETURN VALUE
144 None
148 progress (_ ("error: ") + s)
149 if verbose_p:
150 raise _ ("Exiting ... ")
151 else:
152 sys.exit (2)
154 def getopt_args (opts):
155 '''Construct arguments (LONG, SHORT) for getopt from list of options.'''
156 short = ''
157 long = []
158 for o in opts:
159 if o[1]:
160 short = short + o[1]
161 if o[0]:
162 short = short + ':'
163 if o[2]:
164 l = o[2]
165 if o[0]:
166 l = l + '='
167 long.append (l)
168 return (short, long)
170 def option_help_str (o):
171 '''Transform one option description (4-tuple ) into neatly formatted string'''
172 sh = ' '
173 if o[1]:
174 sh = '-%s' % o[1]
176 sep = ' '
177 if o[1] and o[2]:
178 sep = ','
180 long = ''
181 if o[2]:
182 long= '--%s' % o[2]
184 arg = ''
185 if o[0]:
186 if o[2]:
187 arg = '='
188 arg = arg + o[0]
189 return ' ' + sh + sep + long + arg
192 def options_help_str (opts):
193 '''Convert a list of options into a neatly formatted string'''
194 w = 0
195 strs =[]
196 helps = []
198 for o in opts:
199 s = option_help_str (o)
200 strs.append ((s, o[3]))
201 if len (s) > w:
202 w = len (s)
204 str = ''
205 for s in strs:
206 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
207 return str
209 def help ():
210 ls = [(_ ("Usage: %s [OPTION]... FILE") % program_name),
211 ('\n\n'),
212 (help_summary),
213 ('\n\n'),
214 (_ ("Options:")),
215 ('\n'),
216 (options_help_str (option_definitions)),
217 ('\n\n'),
218 (_ ("Report bugs to %s") % 'bug-lilypond@gnu.org'),
219 ('\n')]
220 map (sys.stdout.write, ls)
222 def setup_temp ():
224 Create a temporary directory, and return its name.
226 global temp_dir
227 if not keep_temp_dir_p:
228 temp_dir = tempfile.mktemp (program_name)
229 try:
230 os.mkdir (temp_dir, 0777)
231 except OSError:
232 pass
234 return temp_dir
237 def system (cmd, ignore_error = 0, quiet =0):
238 """Run CMD. If IGNORE_ERROR is set, don't complain when CMD returns non zero.
240 RETURN VALUE
242 Exit status of CMD
245 if verbose_p:
246 progress (_ ("Invoking `%s\'") % cmd)
248 st = os.system (cmd)
249 if st:
250 name = re.match ('[ \t]*([^ \t]*)', cmd).group (1)
251 msg = name + ': ' + _ ("command exited with value %d") % st
252 if ignore_error:
253 if not quiet or verbose_p:
254 warning (msg + ' ' + _ ("(ignored)") + ' ')
255 else:
256 error (msg)
258 return st
261 def cleanup_temp ():
262 if not keep_temp_dir_p:
263 if verbose_p:
264 progress (_ ("Cleaning %s...") % temp_dir)
265 shutil.rmtree (temp_dir)
268 def strip_extension (f, ext):
269 (p, e) = os.path.splitext (f)
270 if e == ext:
271 e = ''
272 return p + e
275 def cp_to_dir (pattern, dir):
276 "Copy files matching re PATTERN from cwd to DIR"
277 # Duh. Python style portable: cp *.EXT OUTDIR
278 # system ('cp *.%s %s' % (ext, outdir), 1)
279 files = filter (lambda x, p=pattern: re.match (p, x), os.listdir ('.'))
280 map (lambda x, d=dir: shutil.copy2 (x, os.path.join (d, x)), files)
283 # Python < 1.5.2 compatibility
285 # On most platforms, this is equivalent to
286 #`normpath(join(os.getcwd()), PATH)'. *Added in Python version 1.5.2*
287 if os.path.__dict__.has_key ('abspath'):
288 abspath = os.path.abspath
289 else:
290 def abspath (path):
291 return os.path.normpath (os.path.join (os.getcwd (), path))
293 if os.__dict__.has_key ('makedirs'):
294 makedirs = os.makedirs
295 else:
296 def makedirs (dir, mode=0777):
297 system ('mkdir -p %s' % dir)
300 def mkdir_p (dir, mode=0777):
301 if not os.path.isdir (dir):
302 makedirs (dir, mode)
305 # if set, LILYPONDPREFIX must take prevalence
306 # if datadir is not set, we're doing a build and LILYPONDPREFIX
307 datadir = '@local_lilypond_datadir@'
309 if os.environ.has_key ('LILYPONDPREFIX') :
310 datadir = os.environ['LILYPONDPREFIX']
311 else:
312 datadir = '@local_lilypond_datadir@'
315 while datadir[-1] == os.sep:
316 datadir= datadir[:-1]
318 sys.path.insert (0, os.path.join (datadir, 'python'))
320 ################################################################
321 # END Library
324 program_name = 'ly2dvi'
326 original_dir = os.getcwd ()
327 temp_dir = os.path.join (original_dir, '%s.dir' % program_name)
328 errorport = sys.stderr
329 keep_temp_dir_p = 0
330 verbose_p = 0
331 preview_p = 0
332 lilypond_error_p = 0
333 preview_resolution = 90
334 pseudo_filter_p = 0
335 latex_cmd = 'latex'
336 tex_extension = '.tex'
337 pdftex_p = 0
339 help_summary = _ ("Run LilyPond using LaTeX for titling")
341 option_definitions = [
342 ('', 'd', 'dependencies',
343 _ ("write Makefile dependencies for every input file")),
344 ('', 'h', 'help', _ ("this help")),
345 (_ ("DIR"), 'I', 'include', _ ("add DIR to LilyPond's search path")),
346 ('', 'k', 'keep',
347 _ ("keep all output, output to directory %s.dir") % program_name),
348 ('', '', 'no-lily', _ ("don't run LilyPond")),
349 ('', 'm', 'no-paper', _ ("produce MIDI output only")),
350 (_ ("FILE"), 'o', 'output', _ ("write ouput to FILE")),
351 (_ ("FILE"), 'f', 'find-pfa', _ ("find pfa fonts used in FILE")),
352 (_ ('RES'), '', 'preview-resolution',
353 _ ("set the resolution of the preview to RES")),
354 ('', 'P', 'postscript', _ ("generate PostScript output")),
355 ('', 'p', 'pdf', _ ("generate PDF output")),
356 ('', '', 'pdftex', _ ("use pdflatex to generate a PDF output")),
357 # FIXME: preview, picture; to indicate creation of a PNG?
358 ('', '', 'preview', _ ("make a picture of the first system")),
359 (_ ("KEY=VAL"), 's', 'set', _ ("change global setting KEY to VAL")),
360 ('', 'V', 'verbose', _ ("verbose")),
361 ('', 'v', 'version', _ ("print version number")),
362 ('', 'w', 'warranty', _ ("show warranty and copyright")),
365 layout_fields = ['dedication', 'title', 'subtitle', 'subsubtitle',
366 'footer', 'head', 'composer', 'arranger', 'instrument',
367 'opus', 'piece', 'metre', 'meter', 'poet', 'texttranslator']
370 # init to empty; values here take precedence over values in the file
372 ## TODO: change name.
373 extra_init = {
374 'language' : [],
375 'latexheaders' : [],
376 'latexpackages' : ['geometry'],
378 # for geometry v3.
379 'latexoptions' : ['compat2'],
380 'papersize' : [],
381 'pagenumber' : [1],
382 'textheight' : [],
383 'linewidth' : [],
384 'orientation' : [],
385 'unit' : ['pt'],
388 extra_fields = extra_init.keys ()
389 fields = layout_fields + extra_fields
391 include_path = ['.']
392 lily_p = 1
393 paper_p = 1
395 output_name = ''
397 # Output formats that ly2dvi should create
398 targets = ['DVI', 'LATEX', 'MIDI', 'TEX']
400 track_dependencies_p = 0
401 dependency_files = []
405 # tex needs lots of memory, more than it gets by default on Debian
406 non_path_environment = {
407 'extra_mem_top' : '1000000',
408 'extra_mem_bottom' : '1000000',
409 'pool_size' : '250000',
412 def command_name (cmd):
413 return re.match ('^[ \t]*([^ \t]*)', cmd).group (1)
415 def error_log (name):
416 return os.path.join (__main__.temp_dir, '%s.errorlog' % name)
418 def read_pipe (cmd, mode = 'r'):
419 redirect = ''
420 if verbose_p:
421 progress (_ ("Opening pipe `%s\'") % cmd)
422 redirect = ' 2>%s' % error_log (command_name (cmd))
423 pipe = os.popen (cmd + redirect, mode)
424 output = pipe.read ()
425 status = pipe.close ()
426 # successful pipe close returns 'None'
427 if not status:
428 status = 0
429 signal = 0x0f & status
430 exit_status = status >> 8
432 if status:
433 sys.stderr.write (_ ("`%s\' failed (%d)") % (cmd, exit_status))
434 if not verbose_p:
435 sys.stderr.write (_ ("The error log is as follows:"))
436 sys.stderr.write (open (error_log (command_name (cmd)).read ()))
437 sys.exit (status)
438 if __main__.verbose_p:
439 progress ('\n')
440 return output
442 def setup_environment ():
443 global environment
445 kpse = read_pipe ('kpsexpand \$TEXMF')
446 texmf = re.sub ('[ \t\n]+$','', kpse)
447 type1_paths = read_pipe ('kpsewhich -expand-path=\$T1FONTS')
449 environment = {
450 # TODO: * prevent multiple addition.
451 # * clean TEXINPUTS, MFINPUTS, TFMFONTS,
452 # as these take prevalence over $TEXMF
453 # and thus may break tex run?
454 'TEXMF' : "{%s,%s}" % (datadir, texmf) ,
455 'GS_FONTPATH' : type1_paths,
456 'GS_LIB' : datadir + '/ps',
459 # $TEXMF is special, previous value is already taken care of
460 if os.environ.has_key ('TEXMF'):
461 del os.environ['TEXMF']
463 for key in environment.keys ():
464 val = environment[key]
465 if os.environ.has_key (key):
466 val = os.environ[key] + os.pathsep + val
467 os.environ[key] = val
469 for key in non_path_environment.keys ():
470 val = non_path_environment[key]
471 os.environ[key] = val
473 #what a name.
474 def set_setting (dict, key, val):
475 try:
476 val = string.atoi (val)
477 except ValueError:
478 #warning (_ ("invalid value: %s") % `val`)
479 pass
481 if type(val) == type ('hoi'):
482 try:
483 val = string.atof (val)
484 except ValueError:
485 #warning (_ ("invalid value: %s") % `val`)
486 pass
488 try:
489 dict[key].append (val)
490 except KeyError:
491 warning (_ ("no such setting: `%s'") % `key`)
492 dict[key] = [val]
495 def print_environment ():
496 for (k,v) in os.environ.items ():
497 sys.stderr.write ("%s=\"%s\"\n" % (k,v))
499 def quiet_system (cmd, name, ignore_error = 0):
500 if not verbose_p:
501 progress ( _("Running %s...") % name)
502 cmd = cmd + ' 1> /dev/null 2> /dev/null'
503 elif pseudo_filter_p:
504 cmd = cmd + ' 1> /dev/null'
506 return system (cmd, ignore_error, quiet = 1)
509 def run_lilypond (files, dep_prefix):
511 opts = ''
512 opts = opts + ' ' + string.join (map (lambda x : '-I ' + x,
513 include_path))
514 if pseudo_filter_p:
515 opts = opts + ' --output=lelie'
516 if paper_p:
517 opts = opts + ' ' + string.join (map (lambda x : '-H ' + x,
518 fields))
519 else:
520 opts = opts + ' --no-paper'
522 if pdftex_p:
523 opts = opts + ' -f pdftex'
525 if track_dependencies_p:
526 opts = opts + " --dependencies"
527 if dep_prefix:
528 opts = opts + ' --dep-prefix=%s' % dep_prefix
530 fs = string.join (files)
532 if not verbose_p:
533 # cmd = cmd + ' 1> /dev/null 2> /dev/null'
534 progress ( _("Running %s...") % 'LilyPond')
535 else:
536 opts = opts + ' --verbose'
538 # for better debugging!
539 print_environment ()
541 cmd = 'lilypond %s %s ' % (opts, fs)
542 if verbose_p:
543 progress ("Invoking `%s'"% cmd)
544 status = os.system (cmd)
546 signal = 0x0f & status
547 exit_status = status >> 8
549 # 2 == user interrupt.
550 if signal and signal != 2:
551 error ("\n\n" + _ ("LilyPond crashed (signal %d).") % signal \
552 + _ ("Please submit a bug report to bug-lilypond@gnu.org") + "\n")
554 if status:
555 sys.stderr.write ( "\n" \
556 + _ ("LilyPond failed on an input file (exit status %d).") % exit_status + "\n")
557 sys.stderr.write (_("Trying to salvage the rest.") +'\n\n')
559 global lilypond_error_p
560 lilypond_error_p = 1
563 def analyse_lilypond_output (filename, extra):
565 # urg
566 '''Grep FILENAME for interesting stuff, and
567 put relevant info into EXTRA.'''
568 filename = filename+tex_extension
569 progress (_ ("Analyzing %s...") % filename)
570 s = open (filename).read ()
572 # search only the first 10k
573 s = s[:10240]
574 for x in extra_fields:
575 m = re.search (r'\\def\\lilypondpaper%s{([^}]*)}'%x, s)
576 if m:
577 set_setting (extra, x, m.group (1))
579 def find_tex_files_for_base (base, extra):
582 Find the \header fields dumped from BASE.
585 headerfiles = {}
586 for f in layout_fields:
587 if os.path.exists (base + '.' + f):
588 headerfiles[f] = base+'.'+f
590 if os.path.exists (base +'.dep'):
591 dependency_files.append (base + '.dep')
593 for f in extra_fields:
594 if os.path.exists (base + '.' + f):
595 extra[f].append (open (base + '.' + f).read ())
597 return (base+tex_extension,headerfiles)
600 def find_tex_files (files, extra):
602 Find all .tex files whose prefixes start with some name in FILES.
606 tfiles = []
608 for f in files:
609 x = 0
610 while 1:
611 fname = os.path.basename (f)
612 fname = strip_extension (fname, '.ly')
613 if x:
614 fname = fname + '-%d' % x
616 if os.path.exists (fname + tex_extension):
617 tfiles.append (find_tex_files_for_base (fname, extra))
618 analyse_lilypond_output (fname, extra)
619 else:
620 break
622 x = x + 1
623 if not x:
624 fstr = string.join (files, ', ')
625 warning (_ ("no LilyPond output found for `%s'") % fstr)
626 return tfiles
628 def one_latex_definition (defn, first):
629 s = '\n'
630 for (k,v) in defn[1].items ():
631 val = open (v).read ()
632 if (string.strip (val)):
633 s = s + r'''\def\lilypond%s{%s}''' % (k, val)
634 else:
635 s = s + r'''\let\lilypond%s\relax''' % k
636 s = s + '\n'
638 if first:
639 s = s + '\\def\\mustmakelilypondtitle{}\n'
640 else:
641 s = s + '\\def\\mustmakelilypondpiecetitle{}\n'
643 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.
644 return s
647 ly_paper_to_latexpaper = {
648 'a4' : 'a4paper',
649 'letter' : 'letterpaper',
650 'a3' : 'a3paper'
653 #TODO: should set textheight (enlarge) depending on papersize.
654 def global_latex_preamble (extra):
655 '''construct preamble from EXTRA,'''
656 s = ""
657 s = s + '% generation tag\n'
659 options = ''
662 if extra['papersize']:
663 try:
664 options = ly_paper_to_latexpaper[extra['papersize'][0]]
665 except KeyError:
666 warning (_ ("invalid value: `%s'") % `extra['papersize'][0]`)
667 pass
669 if extra['latexoptions']:
670 options = options + ',' + extra['latexoptions'][-1]
672 s = s + '\\documentclass[%s]{article}\n' % options
674 if extra['language']:
675 s = s + r'\usepackage[%s]{babel}' % extra['language'][-1] + '\n'
678 s = s + '\\usepackage{%s}\n' \
679 % string.join (extra['latexpackages'], ',')
681 if extra['latexheaders']:
682 s = s + '\\include{%s}\n' \
683 % string.join (extra['latexheaders'], '}\n\\include{')
685 unit = extra['unit'][-1]
687 textheight = ''
688 if extra['textheight']:
689 textheight = ',textheight=%f%s' % (extra['textheight'][0], unit)
691 orientation = 'portrait'
692 if extra['orientation']:
693 orientation = extra['orientation'][0]
695 # set sane geometry width (a4-width) for linewidth = -1.
696 maxlw = max (extra['linewidth'] + [-1])
697 if maxlw < 0:
698 # who the hell is 597 ?
699 linewidth = '597pt'
700 else:
701 linewidth = '%d%s' % (maxlw, unit)
702 s = s + '\geometry{width=%s%s,headheight=2mm,footskip=2mm,%s}\n' % (linewidth, textheight, orientation)
704 if extra['latexoptions']:
705 s = s + '\geometry{twosideshift=4mm}\n'
707 s = s + r'''
708 \usepackage[latin1]{inputenc}
709 \input{titledefs}
712 if extra['pagenumber'] and extra['pagenumber'][-1] and extra['pagenumber'][-1] != 'no':
713 s = s + '\setcounter{page}{%d}\n' % (extra['pagenumber'][-1])
714 s = s + '\\pagestyle{plain}\n'
715 else:
716 s = s + '\\pagestyle{empty}\n'
719 return s
722 def global_latex_definition (tfiles, extra):
723 '''construct preamble from EXTRA, dump Latex stuff for each
724 lily output file in TFILES after that, and return the Latex file constructed. '''
727 s = global_latex_preamble (extra) + '\\begin{document}\n'
728 s = s + '\\parindent 0pt\n'
729 s = s + '\\thispagestyle{firstpage}\n'
731 first = 1
732 for t in tfiles:
733 s = s + one_latex_definition (t, first)
734 first = 0
737 s = s + '\\thispagestyle{lastpage}\n'
738 s = s + '\\end{document}'
740 return s
742 def run_latex (files, outbase, extra):
744 """Construct latex file, for FILES and EXTRA, dump it into
745 OUTBASE.latex. Run LaTeX on it.
747 RETURN VALUE
749 None
752 latex_fn = outbase + '.latex'
754 wfs = find_tex_files (files, extra)
755 s = global_latex_definition (wfs, extra)
757 f = open (latex_fn, 'w')
758 f.write (s)
759 f.close ()
761 cmd = latex_cmd + ' \\\\nonstopmode \\\\input %s' % latex_fn
762 status = quiet_system (cmd, 'LaTeX', ignore_error = 1)
764 signal = 0xf & status
765 exit_stat = status >> 8
767 if exit_stat:
768 logstr = open (outbase + '.log').read()
769 m = re.search ("\n!", logstr)
770 start = m.start (0)
771 logstr = logstr[start:start+200]
773 user_error (_ ("LaTeX failed on the output file."), 0)
774 sys.stderr.write ("\n")
775 user_error (_ ("The error log is as follows:"), 0)
776 sys.stderr.write ("\n")
777 sys.stderr.write (logstr)
778 sys.stderr.write ("\n")
779 raise 'LaTeX error'
781 if preview_p:
782 # make a preview by rendering only the 1st line.
783 preview_fn = outbase + '.preview.tex'
784 f = open (preview_fn, 'w')
785 f.write (r'''
787 \input lilyponddefs
788 \pagestyle{empty}
789 \begin{document}
790 \def\interscoreline{\endinput}
791 \input %s
792 \end{document}
793 ''' % (global_latex_preamble (extra), outbase))
795 f.close()
796 cmd = '%s \\\\nonstopmode \\\\input %s' % (latex_cmd, preview_fn)
797 quiet_system (cmd, '%s for preview' % latex_cmd)
800 def run_dvips (outbase, extra):
803 """Run dvips using the correct options taken from EXTRA,
804 leaving a PS file in OUTBASE.ps
806 RETURN VALUE
808 None.
810 opts = ''
811 if extra['papersize']:
812 opts = opts + ' -t%s' % extra['papersize'][0]
814 if extra['orientation'] and extra['orientation'][0] == 'landscape':
815 opts = opts + ' -tlandscape'
817 if 'PDF' in targets:
818 opts = opts + ' -Ppdf -G0 -u +lilypond.map'
820 cmd = 'dvips %s -o%s %s' % (opts, outbase + '.ps', outbase + '.dvi')
821 quiet_system (cmd, 'dvips')
823 if preview_p:
824 cmd = 'dvips -E -o%s %s' % ( outbase + '.preview.ps', outbase + '.preview.dvi')
825 quiet_system (cmd, 'dvips for preview')
827 if 'PDF' in targets:
828 cmd = 'ps2pdf %s.ps %s.pdf' % (outbase , outbase)
829 quiet_system (cmd, 'ps2pdf')
831 def get_bbox (filename):
832 # cut & paste
833 system ('gs -sDEVICE=bbox -q -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1 ' % (filename, filename))
835 box = open (filename + '.bbox').read()
836 m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box)
837 gr = []
838 if m:
839 gr = map (string.atoi, m.groups ())
841 return gr
844 # cut & paste from lilypond-book.
846 def make_preview (name, extra):
847 bbox = get_bbox (name + '.preview.ps')
848 margin = 0
849 fo = open (name + '.trans.eps' , 'w')
850 fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
851 fo.close ()
853 x = (2* margin + bbox[2] - bbox[0]) * preview_resolution / 72.
854 y = (2* margin + bbox[3] - bbox[1]) * preview_resolution / 72.
856 cmd = r'''gs -g%dx%d -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit | pnmtopng > %s'''
858 cmd = cmd % (x, y, preview_resolution, name + '.trans.eps', name + '.preview.ps',name + '.png')
859 quiet_system (cmd, 'gs')
861 try:
862 status = system (cmd)
863 except:
864 os.unlink (name + '.png')
865 error ("Removing output file")
869 def generate_dependency_file (depfile, outname):
870 df = open (depfile, 'w')
871 df.write (outname + ':' )
873 for d in dependency_files:
874 s = open (d).read ()
875 s = re.sub ('#[^\n]*\n', '', s)
876 s = re.sub (r'\\\n', ' ', s)
877 m = re.search ('.*:(.*)\n', s)
879 # ugh. Different targets?
880 if m:
881 df.write ( m.group (1) + ' ' )
883 df.write ('\n')
884 df.close ();
886 def find_file_in_path (path, name):
887 for d in string.split (path, os.pathsep):
888 if name in os.listdir (d):
889 return os.path.join (d, name)
891 # Added as functionality to ly2dvi, because ly2dvi may well need to do this
892 # in future too.
893 PS = '%!PS-Adobe'
894 def find_pfa_fonts (name):
895 s = open (name).read ()
896 if s[:len (PS)] != PS:
897 # no ps header?
898 user_error (_ ("not a PostScript file: `%s\'" % name))
899 here = 0
900 m = re.match ('.*?/(feta[-a-z0-9]+) +findfont', s[here:], re.DOTALL)
901 pfa = []
902 while m:
903 here = m.end (1)
904 pfa.append (m.group (1))
905 m = re.match ('.*?/(feta[-a-z0-9]+) +findfont', s[here:], re.DOTALL)
906 return pfa
909 (sh, long) = getopt_args (option_definitions)
910 try:
911 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
912 except getopt.error, s:
913 errorport.write ('\n')
914 user_error (_ ("getopt says: `%s\'" % s), 0)
915 errorport.write ('\n')
916 help ()
917 sys.exit (2)
919 for opt in options:
920 o = opt[0]
921 a = opt[1]
923 if 0:
924 pass
925 elif o == '--help' or o == '-h':
926 help ()
927 sys.exit (0)
928 elif o == '--find-pfa' or o == '-f':
929 fonts = map (lambda x: x + '.pfa', find_pfa_fonts (a))
930 files = map (lambda x:
931 find_file_in_path (os.environ['GS_FONTPATH'], x),
932 fonts)
933 print string.join (files, ' ')
934 sys.exit (0)
935 elif o == '--include' or o == '-I':
936 include_path.append (a)
937 elif o == '--postscript' or o == '-P':
938 targets.append ('PS')
939 elif o == '--pdf' or o == '-p':
940 targets.append ('PS')
941 targets.append ('PDF')
942 elif o == '--keep' or o == '-k':
943 keep_temp_dir_p = 1
944 elif o == '--no-lily':
945 lily_p = 0
946 elif o == '--preview':
947 preview_p = 1
948 targets.append ('PNG')
949 elif o == '--preview-resolution':
950 preview_resolution = string.atoi (a)
951 elif o == '--no-paper' or o == '-m':
952 targets = ['MIDI']
953 paper_p = 0
954 elif o == '--output' or o == '-o':
955 output_name = a
956 elif o == '--set' or o == '-s':
957 ss = string.split (a, '=')
958 set_setting (extra_init, ss[0], ss[1])
959 elif o == '--dependencies' or o == '-d':
960 track_dependencies_p = 1
961 elif o == '--verbose' or o == '-V':
962 verbose_p = 1
963 elif o == '--version' or o == '-v':
964 identify ()
965 sys.exit (0)
966 elif o == '--pdftex':
967 latex_cmd = 'pdflatex'
968 targets.remove('DVI')
969 targets.append('PDFTEX')
970 pdftex_p = 1
971 tex_extension = '.pdftex'
972 elif o == '--warranty' or o == '-w':
973 status = system ('lilypond -w', ignore_error = 1)
974 if status:
975 warranty ()
977 sys.exit (0)
979 # Don't convert input files to abspath, rather prepend '.' to include
980 # path.
981 include_path.insert (0, '.')
983 # As a neat trick, add directory part of first input file
984 # to include path. That way you can do without the clumsy -I in:
986 # ly2dvi -I foe/bar/baz foo/bar/baz/baz.ly
987 if files and files[0] != '-' and os.path.dirname (files[0]) != '.':
988 include_path.append (os.path.dirname (files[0]))
990 include_path = map (abspath, include_path)
992 if files and (files[0] == '-' or output_name == '-'):
993 if len (files) == 1:
994 pseudo_filter_p = 1
995 output_name = 'lelie'
996 if verbose_p:
997 progress (_ ("pseudo filter"))
998 else:
999 help ()
1000 user_error (_ ("pseudo filter only for single input file"), 2)
1003 original_output = output_name
1005 if files:
1007 # Ugh, maybe make a setup () function
1008 files = map (lambda x: strip_extension (x, '.ly'), files)
1010 # hmmm. Wish I'd 've written comments when I wrote this.
1011 # now it looks complicated.
1013 (outdir, outbase) = ('','')
1014 if not output_name:
1015 outbase = os.path.basename (files[0])
1016 outdir = abspath ('.')
1017 elif output_name[-1] == os.sep:
1018 outdir = abspath (output_name)
1019 outbase = os.path.basename (files[0])
1020 else:
1021 (outdir, outbase) = os.path.split (abspath (output_name))
1023 for i in ('.dvi', '.latex', '.ly', '.ps', '.tex', '.pdftex'):
1024 output_name = strip_extension (output_name, i)
1025 outbase = strip_extension (outbase, i)
1027 for i in files[:] + [output_name]:
1028 if string.find (i, ' ') >= 0:
1029 user_error (_ ("filename should not contain spaces: `%s'") % i)
1031 if os.path.dirname (output_name) != '.':
1032 dep_prefix = os.path.dirname (output_name)
1033 else:
1034 dep_prefix = 0
1036 reldir = os.path.dirname (output_name)
1037 if outdir != '.' and (track_dependencies_p or targets):
1038 mkdir_p (outdir, 0777)
1040 tmpdir = setup_temp ()
1041 setup_environment ()
1043 # to be sure, add tmpdir *in front* of inclusion path.
1044 #os.environ['TEXINPUTS'] = tmpdir + ':' + os.environ['TEXINPUTS']
1045 os.chdir (tmpdir)
1047 if lily_p:
1048 try:
1049 run_lilypond (files, dep_prefix)
1050 except:
1051 # TODO: friendly message about LilyPond setup/failing?
1053 # TODO: lilypond should fail with different
1054 # error codes for:
1055 # - guile setup/startup failure
1056 # - font setup failure
1057 # - init.ly setup failure
1058 # - parse error in .ly
1059 # - unexpected: assert/core dump
1060 targets = []
1061 traceback.print_exc ()
1063 # Our LilyPond pseudo filter always outputs to 'lelie'
1064 # have subsequent stages and use 'lelie' output.
1065 if pseudo_filter_p:
1066 files[0] = 'lelie'
1068 if 'PNG' in targets and 'PS' not in targets:
1069 targets.append ('PS')
1070 if 'PS' in targets and 'DVI' not in targets:
1071 targets.append('DVI')
1073 if 'DVI' in targets:
1074 try:
1075 run_latex (files, outbase, extra_init)
1076 # unless: add --tex, or --latex?
1077 targets.remove ('TEX')
1078 targets.remove('LATEX')
1079 except:
1080 # TODO: friendly message about TeX/LaTeX setup,
1081 # trying to run tex/latex by hand
1082 if 'DVI' in targets:
1083 targets.remove ('DVI')
1084 if 'PS' in targets:
1085 targets.remove ('PS')
1086 traceback.print_exc ()
1088 if 'PS' in targets:
1089 try:
1090 run_dvips (outbase, extra_init)
1091 except:
1092 if 'PS' in targets:
1093 targets.remove ('PS')
1094 traceback.print_exc ()
1096 if 'PNG' in targets:
1097 make_preview (outbase, extra_init)
1099 if 'PDFTEX' in targets:
1100 try:
1101 run_latex (files, outbase, extra_init)
1102 # unless: add --tex, or --latex?
1103 targets.remove ('TEX')
1104 targets.remove ('LATEX')
1105 targets.remove ('PDFTEX')
1106 if 'PDF' not in targets:
1107 targets.append('PDF')
1108 except:
1109 # TODO: friendly message about TeX/LaTeX setup,
1110 # trying to run tex/latex by hand
1111 if 'PDFTEX' in targets:
1112 targets.remove ('PDFTEX')
1113 if 'PDF' in targets:
1114 targets.remove ('PDF')
1115 if 'PS' in targets:
1116 targets.remove ('PS')
1117 traceback.print_exc ()
1118 sys.exit(1)
1120 # add DEP to targets?
1121 if track_dependencies_p:
1122 depfile = os.path.join (outdir, outbase + '.dep')
1123 generate_dependency_file (depfile, depfile)
1124 if os.path.isfile (depfile):
1125 progress (_ ("dependencies output to `%s'...") %
1126 depfile)
1128 if pseudo_filter_p:
1129 main_target = 0
1130 for i in 'PDF', 'PS', 'PNG', 'DVI', 'LATEX':
1131 if i in targets:
1132 main_target = i
1133 break
1135 outname = outbase + '.' + string.lower (main_target)
1136 if os.path.isfile (outname):
1137 sys.stdout.write (open (outname).read ())
1138 elif verbose_p:
1139 warning (_ ("can't find file: `%s'") % outname)
1140 targets = []
1142 # Hmm, if this were a function, we could call it the except: clauses
1143 for i in targets:
1144 ext = string.lower (i)
1145 cp_to_dir ('.*\.%s$' % ext, outdir)
1146 outname = outbase + '.' + string.lower (i)
1147 abs = os.path.join (outdir, outname)
1148 if reldir != '.':
1149 outname = os.path.join (reldir, outname)
1150 if os.path.isfile (abs):
1151 progress (_ ("%s output to `%s'...") % (i, outname))
1152 elif verbose_p:
1153 warning (_ ("can't find file: `%s'") % outname)
1155 os.chdir (original_dir)
1156 cleanup_temp ()
1158 sys.exit (lilypond_error_p)
1159 else:
1160 help ()
1161 user_error (_ ("no files specified on command line"), 2)