2 # All non-english comments are NOT in swedish, they are norwegian!
4 # TODO: center option (??)
5 # * the verbatim option should not be visible in the created latex file
6 # * what the h.. does castingalgorithm do/mean???
7 # * the following fails because mudelabook doesn't care that the
8 # last } after \end{mudela} finishes the marginpar:
10 # \begin{mudela}[fragment]
13 # * fragments should know about margins
19 # much rewritten by new author. I think the work has been split more
20 # logical between different classes.
23 # - speedup, do all mudela parsing at the same time to avoid multiple
24 # guile startup that takes some seconds on my computer
33 program_version
= '0.4.1'
37 fontsize_i2a
= {11:'eleven', 13:'thirteen', 16:'sixteen', 20:'twenty', 26:'twentysix'}
38 fontsize_pt2i
= {'11pt':11, '13pt':13, '16pt':16, '20pt':20, '26pt':26}
40 begin_mudela_re
= re
.compile ('^ *\\\\begin{mudela}')
41 extract_papersize_re
= re
.compile('\\\\documentclass[\[, ]*(\w*)paper[\w ,]*\]\{\w*\}')
42 extract_fontsize1_re
= re
.compile('\\\\documentclass\[[^\]]*\]\{[^\}]*\}')
43 extract_fontsize2_re
= re
.compile('[ ,\[]*([0-9]*)pt')
44 begin_mudela_opts_re
= re
.compile('\[[^\]]*\]')
45 end_mudela_re
= re
.compile ('^ *\\\\end{mudela}')
46 section_re
= re
.compile ('\\\\section')
47 chapter_re
= re
.compile ('\\\\chapter')
48 input_re
= re
.compile ('^\\\\input{([^}]*)')
49 include_re
= re
.compile ('^\\\\include{([^}]*)')
50 begin_document_re
= re
.compile ('^ *\\\\begin{document}')
51 documentclass_re
= re
.compile('\\\\documentclass')
52 twocolumn_re
= re
.compile('\\\\twocolumn')
53 onecolumn_re
= re
.compile('\\\\onecolumn')
54 preMudelaExample_re
= re
.compile('\\\\def\\\\preMudelaExample')
55 postMudelaExample_re
= re
.compile('\\\\def\\\\postMudelaExample')
56 boundingBox_re
= re
.compile('%%BoundingBox: ([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)')
58 def file_exist_b(name
):
66 def ps_dimention(fname
):
68 lines
= fd
.readlines()
70 s
= boundingBox_re
.search(line
)
73 return (int(s
.groups()[2])-int(s
.groups()[0]),
74 int(s
.groups()[3])-int(s
.groups()[1]))
79 class SomethingIsSeriouslyBroken
:
82 def file_mtime (name
):
83 return os
.stat (name
)[8] #mod time
85 def need_recompile_b(infile
, outfile
):
86 indate
= file_mtime (infile
)
88 outdate
= file_mtime (outfile
)
89 return indate
> outdate
94 # executes os.system(command) if infile is newer than
95 # outfile or outfile don't exist
97 def compile (command
, workingdir
, infile
, outfile
):
98 "Test if infile is newer than outfile. If so, cd to workingdir"
100 indate
= file_mtime (workingdir
+infile
)
102 outdate
= file_mtime (workingdir
+outfile
)
103 recompile
= indate
> outdate
109 sys
.stderr
.write ('invoking `%s\'\n' % command
)
111 status
= os
.system (command
)
113 status
= os
.system ('cd %s; %s' %(workingdir
, command
))
123 1: {'a4':{10: 345, 11: 360, 12: 390},
124 'a5':{10: 276, 11: 276, 12: 276},
125 'b5':{10: 345, 11: 356, 12: 356},
126 'letter':{10: 345, 11: 360, 12: 390},
127 'legal': {10: 345, 11: 360, 12: 390},
128 'executive':{10: 345, 11: 360, 12: 379}},
129 2: {'a4':{10: 167, 11: 175, 12: 190},
130 'a5':{10: 133, 11: 133, 12: 133},
131 'b5':{10: 167, 11: 173, 12: 173},
132 'letter':{10: 167, 11: 175, 12: 190},
133 'legal':{10: 167, 11: 175, 12: 190},
134 'executive':{10: 167, 11: 175, 12: 184}}}
135 # >0 --> force all mudela to this pt size
136 self
.force_mudela_fontsize
= 0
137 self
.force_verbatim_b
= 0
139 'mudela-fontsize' : {'init': 16},
140 'papersize' : {'init': 'a4'},
141 'num-column' : {'init': 1},
142 'tex-fontsize' : {'init': 11}
144 def clear_for_new_file(self
):
145 for var
in self
.__data
.keys():
146 self
.__data
[var
] = {'init': self
.__data
[var
]['init']}
147 def clear_for_new_block(self
):
148 for var
in self
.__data
.keys():
149 if self
.__data
[var
].has_key('block'):
150 del self
.__data
[var
]['block']
151 def __get_variable(self
, var
):
152 if self
.__data
[var
].has_key('block'):
153 return self
.__data
[var
]['block']
154 elif self
.__data
[var
].has_key('file'):
155 return self
.__data
[var
]['file']
157 return self
.__data
[var
]['init']
158 def setPapersize(self
, size
, requester
):
159 self
.__data
['papersize'][requester
] = size
160 def getPapersize(self
):
161 return self
.__get
_variable
('papersize')
162 def setMudelaFontsize(self
, size
, requester
):
163 self
.__data
['mudela-fontsize'][requester
] = size
164 def getMudelaFontsize(self
):
165 if self
.force_mudela_fontsize
:
166 return self
.force_mudela_fontsize
167 return self
.__get
_variable
('mudela-fontsize')
168 def setTexFontsize(self
, size
, requester
):
169 self
.__data
['tex-fontsize'][requester
] = size
170 def getTexFontsize(self
):
171 return self
.__get
_variable
('tex-fontsize')
172 def setNumColumn(self
, num
, requester
):
173 self
.__data
['num-column'][requester
] = num
174 def getNumColumn(self
):
175 return self
.__get
_variable
('num-column')
176 def getLineWidth(self
):
177 return self
.__linewidth
[self
.getNumColumn()][self
.getPapersize()][self
.getTexFontsize()]
181 def __init__ (self
, basename
):
182 self
.basename
= basename
183 self
.temp_filename
= "%s/%s" %(outdir
, 'mudela-temp.ly')
184 self
.file = open (self
.temp_filename
, 'w')
186 self
.graphic_type
= 'tex'
188 def write (self
, line
):
189 # match only if there is nothing but whitespace before \begin HACK
190 if re
.search('^\s*\\\\begin{mudela}', line
):
191 self
.scan_begin_statement(line
)
192 self
.write_red_tape()
194 self
.file.write (line
)
195 def scan_begin_statement(self
, line
):
196 r
= begin_mudela_opts_re
.search(line
)
198 o
= r
.group()[1:][:-1]
199 optlist
= re
.compile('[ ,]*').split(o
)
202 if 'floating' in optlist
:
203 self
.graphic_type
= 'eps'
205 self
.graphic_type
= 'tex'
206 if 'fragment' in optlist
:
210 for pt
in fontsize_pt2i
.keys():
212 Props
.setMudelaFontsize(fontsize_pt2i
[pt
], 'block')
213 def write_red_tape(self
):
214 self
.file.write ('\\include \"paper%d.ly\"\n' % Props
.getMudelaFontsize())
215 s
= fontsize_i2a
[Props
.getMudelaFontsize()]
217 self
.file.write("\\paper {" # mudela 1.0.4
218 + "\\paper_%s\n linewidth = -1.\\pt;" % s
219 + "castingalgorithm = \Wordwrap; indent = 2.\cm; \n}")
220 self
.file.write("\\score{\n\\notes{")
222 self
.file.write ("\paper {"
223 + "\\paper_%s\n linewidth = %i.\\pt;" % \
224 (s
, Props
.getLineWidth()) \
225 + "castingalgorithm = \Wordwrap; indent = 2.\cm;\n}")
228 self
.file.write ('}\\paper { } }\n')
231 inf
= outdir
+ self
.basename
+ '.ly'
232 outf
= outdir
+ self
.basename
+ '.tex'
233 if not os
.path
.isfile(inf
):
236 status
= os
.system ('diff -q %s %s' % (self
.temp_filename
, inf
))
238 os
.rename (self
.temp_filename
, inf
)
239 if need_recompile_b(inf
, outf
):
240 out_files
.append((self
.graphic_type
, inf
))
241 def insert_me_string(self
):
242 "Returns a string that can be used directly in latex."
243 if self
.graphic_type
== 'tex':
244 return ['tex', self
.basename
]
245 elif self
.graphic_type
== 'eps':
246 return ['eps', self
.basename
]
248 raise SomethingIsSeriouslyBroken
251 def __init__ (self
, name
):
252 self
.output_fn
= '%s/%s' % (outdir
, name
)
254 def open_verbatim (self
):
255 self
.__lines
.append('\\begin{verbatim}\n')
256 def close_verbatim (self
):
257 self
.__lines
.append('\\end{verbatim}\n')
259 self
.__lines
.append(s
)
260 def create_graphics(self
):
263 for line
in self
.__lines
:
264 if type(line
)==type([]):
267 if need_recompile_b(outdir
+g
[1]+'.ly', outdir
+g
[1]+'.tex'):
268 s
= s
+ ' ' + g
[1]+'.ly'
270 os
.system('cd %s; lilypond %s' %(outdir
, s
))
273 compile('tex %s' % g
[1]+'.tex', outdir
, g
[1]+'.tex', g
[1]+'.dvi')
274 compile('dvips -E -o %s %s' %(g
[1]+'.eps', g
[1]+'.dvi'), outdir
,
275 g
[1]+'.dvi', g
[1]+'.eps')
276 def write_outfile(self
):
277 outfn
= self
.output_fn
+'.latex'
278 sys
.stderr
.write ('Writing output to `%s\'\n'% outfn
)
279 file = open(outfn
, 'w')
280 file.write('% Created by mudela-book\n')
281 for line
in self
.__lines
:
282 if type(line
)==type([]):
284 file.write('\\preMudelaExample\\input %s\n\postMudelaExample '\
285 # % (outdir+line[1]+'.tex'))
287 # TeX applies the prefix of the main source automatically.
290 ps_dim
= ps_dimention(outdir
+line
[1]+'.eps')
291 file.write('\\parbox{%ipt}{\includegraphics{%s}}\n' \
292 # % (ps_dim[0], outdir+line[1]+'.eps'))
293 % (ps_dim
[0], line
[1]+'.eps'))
299 def __init__ (self
, filename
):
300 for fn
in [filename
, filename
+'.tex', filename
+'.doc']:
302 self
.infile
= open (fn
)
307 def get_lines (self
):
308 lines
= self
.infile
.readlines ()
309 (retlines
, retdeps
) = ([],[self
.filename
])
311 r_inp
= input_re
.search (line
)
312 r_inc
= include_re
.search (line
)
313 # Filename rules for \input :
315 # 1. will search for file with exact that name (tex-input.my will be found)
316 # 2. will search for file with .tex ext, (tex-input.my
317 # will find tex-input.my.tex)
318 # input: with .tex ext
319 # 1. will find exact match
321 # Filename rules for \include :
322 # 1. will only find files with name given to \include + .tex ext
324 t
= Tex_input (r_inp
.groups()[0])
326 retlines
= retlines
+ ls
[0]
327 retdeps
= retdeps
+ ls
[1]
329 t
= Tex_input (r_inc
.groups()[0]+'.tex')
331 ls
[0].insert(0, '\\newpage\n')
332 ls
[0].append('\\newpage\n')
333 retlines
= retlines
+ ls
[0]
334 retdeps
= retdeps
+ ls
[1]
336 retlines
.append (line
)
337 return (retlines
, retdeps
)
340 class Main_tex_input(Tex_input
):
341 def __init__ (self
, name
, outname
):
343 Tex_input
.__init
__ (self
, name
) # ugh
344 self
.outname
= outname
348 self
.mudtex
= Tex_output (self
.outname
)
352 # set to 'mudela' when we are processing mudela code,
353 # both verbatim and graphic-to-be
355 def set_sections (self
, l
):
356 if section_re
.search (l
):
357 self
.section
= self
.section
+ 1
358 if chapter_re
.search (l
):
360 self
.chapter
= self
.chapter
+ 1
362 def gen_basename (self
):
363 return '%s-%d.%d.%d' % (self
.outname
,self
.chapter
,self
.section
,self
.fine_count
)
364 #return '%s/%s-%d.%d.%d' % (outdir, self.outname,self.chapter,self.section,self.fine_count)
366 def extract_papersize_from_documentclass(self
, line
):
367 pre
= extract_papersize_re
.search(line
)
370 return pre
.groups()[0]
371 def extract_fontsize_from_documentclass(self
, line
):
372 if extract_fontsize1_re
.search(line
):
373 r
= extract_fontsize2_re
.search(line
)
375 return int(r
.groups()[0])
377 preMudelaDef
= postMudelaDef
= 0
378 (lines
, self
.deps
) = self
.get_lines ()
380 if documentclass_re
.search (line
):
381 p
= self
.extract_papersize_from_documentclass (line
)
383 Props
.setPapersize(p
, 'file')
384 f
= self
.extract_fontsize_from_documentclass (line
)
386 Props
.setTexFontsize (f
, 'file')
387 elif twocolumn_re
.search (line
):
388 Props
.setNumColumn (2, 'file')
389 elif onecolumn_re
.search (line
):
390 Props
.setNumColumn (1, 'file')
391 elif preMudelaExample_re
.search (line
):
393 elif postMudelaExample_re
.search (line
):
395 elif begin_document_re
.search (line
):
397 self
.mudtex
.write ('\\def\\preMudelaExample{}\n')
398 if not postMudelaDef
:
399 self
.mudtex
.write ('\\def\\postMudelaExample{}\n')
400 elif begin_mudela_re
.search (line
):
401 Props
.clear_for_new_block()
403 if self
.mode
== 'mudela':
406 r
= begin_mudela_opts_re
.search (line
)
408 o
= r
.group()[1:][:-1]
409 optlist
= re
.compile('[ ,]*').split(o
)
412 if ('verbatim' in optlist
) or (Props
.force_verbatim_b
):
414 self
.mudtex
.open_verbatim ()
417 self
.mudela
= Mudela_output (self
.gen_basename ())
419 elif end_mudela_re
.search (line
):
421 if self
.mode
!= 'mudela':
425 self
.mudtex
.write (self
.mudela
.insert_me_string())
428 self
.fine_count
= self
.fine_count
+ 1
430 self
.mudtex
.write (line
)
431 self
.mudtex
.close_verbatim ()
435 if self
.mode
== 'mudela' and not self
.verbatim
:
436 self
.mudela
.write (line
)
438 self
.mudtex
.write (line
)
439 self
.set_sections(line
)
440 self
.mudtex
.create_graphics()
441 self
.mudtex
.write_outfile()
446 sys
.stdout
.write("""Usage: mudela-book [options] FILE\n
447 Generate hybrid LaTeX input from Latex + mudela
449 -h, --help print this help
450 -d, --outdir=DIR directory to put generated files
451 -o, --outname=FILE prefix for filenames
452 --default-mudela-fontsize=??pt default fontsize for music
453 --force-mudela-fontsize=??pt force fontsize for all inline mudela
454 --force-verbatim make all mudela verbatim\n"""
459 def write_deps (fn
, out
, deps
):
460 out_fn
= outdir
+ '/' + fn
461 out_fn
= re
.sub ('//', '/', out_fn
)
462 print 'writing `%s\'\n' % out_fn
464 f
= open (out_fn
, 'w')
465 f
.write ('%s: %s\n'% (outdir
+ '/' + out
+ '.dvi',
466 reduce (lambda x
,y
: x
+ ' '+ y
, deps
)))
470 sys
.stderr
.write ('This is %s version %s\n' % ('mudela-book', program_version
))
476 (options
, files
) = getopt
.getopt(
477 sys
.argv
[1:], 'hd:o:', ['outdir=', 'outname=',
478 'default-mudela-fontsize=',
479 'force-mudela-fontsize=',
480 'help', 'dependencies',
482 except getopt
.error
, msg
:
490 if o
== '--outname' or o
== '-o':
493 print "Mudela-book is confused by --outname on multiple files"
496 if o
== '--outdir' or o
== '-d':
498 if o
== '--help' or o
== '-h':
500 if o
== '--dependencies':
502 if o
== '--default-mudela-fontsize':
503 if not fontsize_pt2i
.has_key(a
):
504 print "Error: illegal fontsize:", a
505 print " accepted fontsizes are: 11pt, 13pt, 16pt, 20pt, 26pt"
507 Props
.setMudelaFontsize(fontsize_pt2i
[a
], 'init')
508 if o
== '--force-mudela-fontsize':
509 if not fontsize_pt2i
.has_key(a
):
510 print "Error: illegal fontsize:", a
511 print " accepted fontsizes are: 11pt, 13pt, 16pt, 20pt, 26pt"
513 Props
.force_mudela_fontsize
= fontsize_pt2i
[a
]
514 if o
== '--force-verbatim':
515 Props
.force_verbatim_b
= 1
517 if outdir
[-1:] != '/':
518 outdir
= outdir
+ '/'
520 if not os
.path
.isdir(outdir
):
521 os
.system('mkdir %s' % outdir
)
523 for input_filename
in files
:
524 Props
.clear_for_new_file()
528 my_outname
= os
.path
.basename(os
.path
.splitext(input_filename
)[0])
529 my_depname
= my_outname
+ '.dep'
530 inp
= Main_tex_input (input_filename
, my_outname
)
535 write_deps (my_depname
, my_outname
, inp
.deps
)