]> git.donarmstrong.com Git - lilypond.git/blob - python/book_texinfo.py
Web-ja: update introduction
[lilypond.git] / python / book_texinfo.py
1 # -*- coding: utf-8 -*-
2
3 import re
4 import tempfile
5 import subprocess
6 import sys
7 import book_base as BookBase
8 from book_snippets import *
9 import lilylib as ly
10 global _;_=ly._
11
12
13 # Recognize special sequences in the input.
14 #
15 #   (?P<name>regex) -- Assign result of REGEX to NAME.
16 #   *? -- Match non-greedily.
17 #   (?!...) -- Match if `...' doesn't match next (without consuming
18 #              the string).
19 #
20 #   (?m) -- Multiline regex: Make ^ and $ match at each line.
21 #   (?s) -- Make the dot match all characters including newline.
22 #   (?x) -- Ignore whitespace in patterns.
23 # Possible keys are:
24 #     'multiline_comment', 'verbatim', 'lilypond_block', 'singleline_comment',
25 #     'lilypond_file', 'include', 'lilypond', 'lilypondversion'
26 TexInfo_snippet_res = {
27     'include': r'''(?mx)
28           ^(?P<match>
29           @include\s+
30            (?P<filename>\S+))''',
31
32     'lilypond': r'''(?smx)
33           ^[^\n]*?(?!@c\s+)[^\n]*?
34           (?P<match>
35           @lilypond\s*(
36           \[
37            \s*(?P<options>.*?)\s*
38           \])?\s*{
39            (?P<code>.*?)
40           })''',
41
42     'lilypond_block': r'''(?msx)
43           ^(?P<match>
44           @lilypond\s*(
45           \[
46            \s*(?P<options>.*?)\s*
47           \])?\s+?
48           ^(?P<code>.*?)
49           ^@end\s+lilypond)\s''',
50
51     'lilypond_file': r'''(?mx)
52           ^(?P<match>
53           @lilypondfile\s*(
54           \[
55            \s*(?P<options>.*?)\s*
56           \])?\s*{
57            (?P<filename>\S+)
58           })''',
59
60     'multiline_comment': r'''(?smx)
61           ^(?P<match>
62            (?P<code>
63            @ignore\s
64             .*?
65            @end\s+ignore))\s''',
66
67     'musicxml_file': r'''(?mx)
68           ^(?P<match>
69           @musicxmlfile\s*(
70           \[
71            \s*(?P<options>.*?)\s*
72           \])?\s*{
73            (?P<filename>\S+)
74           })''',
75
76     'singleline_comment': r'''(?mx)
77           ^.*
78           (?P<match>
79            (?P<code>
80            @c([ \t][^\n]*|)\n))''',
81
82     # Don't do this: It interferes with @code{@{}.
83     #        'verb': r'''(?P<code>@code{.*?})''',
84
85     'verbatim': r'''(?sx)
86           (?P<match>
87            (?P<code>
88            @example
89             \s.*?
90            @end\s+example\s))''',
91
92     'lilypondversion': r'''(?mx)
93          [^@](?P<match>
94           @lilypondversion)[^a-zA-Z]''',
95
96 }
97
98
99 TexInfo_output = {
100     ADDVERSION: r'''@example
101 \version @w{"@version{}"}
102 @end example
103 ''',
104
105     FILTER: r'''@lilypond[%(options)s]
106 %(code)s
107 @lilypond''',
108
109     OUTPUT: r'''
110 @iftex
111 @include %(base)s-systems.texi
112 @end iftex
113 ''',
114
115     OUTPUTIMAGE: r'''@noindent
116 @ifinfo
117 @image{%(info_image_path)s,,,%(alt)s,}
118 @end ifinfo
119 @html
120 <p>
121  <a href="%(base)s%(ext)s">
122   <img align="middle"
123        border="0"
124        src="%(image)s"
125        alt="%(alt)s">
126  </a>
127 </p>
128 @end html
129 ''',
130
131     PRINTFILENAME: '''
132 @html
133 <a href="%(base)s%(ext)s">
134 @end html
135 @file{%(filename)s}
136 @html
137 </a>
138 @end html
139     ''',
140
141     QUOTE: r'''@quotation
142 %(str)s@end quotation
143 ''',
144
145     VERBATIM: r'''@exampleindent 0
146 %(version)s@verbatim
147 %(verb)s@end verbatim
148 ''',
149
150     VERSION: r'''%(program_version)s''',
151 }
152
153
154 texinfo_line_widths = {
155     '@afourpaper': '160\\mm',
156     '@afourwide': '6.5\\in',
157     '@afourlatex': '150\\mm',
158     '@smallbook': '5\\in',
159     '@letterpaper': '6\\in',
160 }
161
162
163 ###
164 # Retrieve dimensions from texinfo
165 TEXINFO_INSPECTION_DOCUMENT = r'''
166 \input texinfo
167 @setfilename Texinfo_width_test
168 @settitle Texinfo width test
169 %(preamble)s
170
171 @message{Global: textwidth=@the@hsize,exampleindent=@the@lispnarrowing}
172
173 dummy
174
175 @bye
176 '''
177
178 def get_texinfo_width_indent (source, global_options):
179     #TODO: Check for end of header command "@c %**end of header"
180     #      only use material before that comment ?
181
182     # extract all relevant papter settings from the input:
183     pagesize = None
184     texinfo_paper_size_regexp = r'''(@(?:afourpaper|afourwide|afourlatex|afivepaper|smallbook|letterpaper))''';
185     m = re.search (texinfo_paper_size_regexp, source);
186     if m:
187         pagesize = m.group (1)
188
189     relevant_settings_regexp = r'''(@(?:fonttextsize|pagesizes|cropmarks|exampleindent).*)\n''';
190     m = re.findall (relevant_settings_regexp, source);
191     if pagesize:
192         m.insert (0, pagesize);
193     # all relevant options to insert into the test document:
194     preamble = "\n".join (m);
195
196     texinfo_document = TEXINFO_INSPECTION_DOCUMENT % {'preamble': preamble}
197
198     (handle, tmpfile) = tempfile.mkstemp('.texi')
199     outfile = os.path.splitext (tmpfile)[0] + '.pdf'
200
201     tmp_handle = os.fdopen (handle,'w')
202     tmp_handle.write (texinfo_document)
203     tmp_handle.close ()
204
205     # Work around a texi2pdf bug: if LANG=C is not given, a broken regexp is
206     # used to detect relative/absolute paths, so the absolute path is not
207     # detected as such and this command fails:
208     progress (_ ("Running texi2pdf on file %s to detect default page settings.\n") % tmpfile);
209
210     # execute the command and pipe stdout to the parameter_string:
211     cmd = '%s -c -o %s %s' % (global_options.texinfo_program, outfile, tmpfile);
212     ly.debug_output ("Executing: %s\n" % cmd);
213     run_env = os.environ.copy()
214     run_env['LC_ALL'] = 'C'
215
216     ### unknown why this is necessary
217     universal_newlines = True
218     if sys.platform == 'mingw32':
219         universal_newlines = False
220         ### use os.system to avoid weird sleep() problems on
221         ### GUB's python 2.4.2 on mingw
222         # make file to write to
223         output_dir = tempfile.mkdtemp()
224         output_filename = os.path.join(output_dir, 'output.txt')
225         # call command
226         cmd += " > %s" % output_filename
227         returncode = os.system(cmd)
228         parameter_string = open(output_filename).read()
229         if returncode != 0:
230             warning (_ ("Unable to auto-detect default settings:\n"))
231         # clean up
232         os.remove(output_filename)
233         os.rmdir(output_dir)
234     else:
235         proc = subprocess.Popen (cmd,
236             env=run_env,
237             universal_newlines=universal_newlines,
238             shell=True,
239             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
240         (parameter_string, error_string) = proc.communicate ()
241         if proc.returncode != 0:
242             warning (_ ("Unable to auto-detect default settings:\n%s")
243                     % error_string)
244     os.unlink (tmpfile)
245     if os.path.exists(outfile):
246         os.unlink (outfile)
247
248     # Find textwidth and exampleindent and format it as \\mm or \\in
249     # Use defaults if they cannot be extracted
250     textwidth = 0
251     m = re.search ('textwidth=([0-9.]+)pt', parameter_string)
252     if m:
253         val = float (m.group (1))/72.27
254         if pagesize and pagesize.startswith ("@afour"):
255             textwidth = "%g\\mm" % round (val*25.4, 3);
256         else:
257             textwidth = "%g\\in" % round (val, 3);
258     else:
259         textwidth = texinfo_line_widths.get(pagesize, "6\\in")
260
261     exampleindent = 0
262     m = re.search ('exampleindent=([0-9.]+)pt', parameter_string)
263     if m:
264         val = float (m.group (1))/72.27
265         if pagesize and pagesize.startswith ("@afour"):
266             exampleindent = "%g\\mm" % round (val*25.4, 3);
267         else:
268             exampleindent = "%g\\in" % round (val, 3);
269     else:
270         exampleindent = "0.4\\in"
271
272     retval = {LINE_WIDTH: textwidth, EXAMPLEINDENT: exampleindent}
273     ly.debug_output ("Auto-detected values are: %s\n" % retval);
274     return retval;
275
276
277
278 texinfo_lang_re = re.compile ('(?m)^@documentlanguage (.*?)( |$)')
279
280 class BookTexinfoOutputFormat (BookBase.BookOutputFormat):
281     def __init__ (self):
282         BookBase.BookOutputFormat.__init__ (self)
283         self.format = "texinfo"
284         self.default_extension = ".texi"
285         self.snippet_res = TexInfo_snippet_res
286         self.output = TexInfo_output
287         self.handled_extensions = ['.itely', '.tely', '.texi', '.texinfo']
288         self.snippet_option_separator = '\s*,\s*'
289
290     def can_handle_format (self, format):
291         return (BookBase.BookOutputFormat.can_handle_format (self, format) or
292                (format in ['texi-html', 'texi']))
293
294     def process_options (self, global_options):
295         self.process_options_pdfnotdefault (global_options)
296
297     def get_document_language (self, source):
298         m = texinfo_lang_re.search (source)
299         if m and not m.group (1).startswith ('en'):
300             return m.group (1)
301         else:
302             return ''
303
304     def init_default_snippet_options (self, source):
305         texinfo_defaults = get_texinfo_width_indent (source, self.global_options);
306         self.default_snippet_options.update (texinfo_defaults)
307         BookBase.BookOutputFormat.init_default_snippet_options (self, source)
308
309     def adjust_snippet_command (self, cmd):
310         if '--formats' not in cmd:
311             return cmd + ' --formats=png '
312         else:
313             return cmd
314
315     def output_info (self, basename, snippet):
316         str = ''
317         rep = snippet.get_replacements ();
318         rep['base'] = basename
319         rep['filename'] = os.path.basename (snippet.filename)
320         rep['ext'] = snippet.ext
321         for image in snippet.get_images ():
322             rep1 = copy.copy (rep)
323             rep1['base'] = os.path.splitext (image)[0]
324             rep1['image'] = image
325             rep1['alt'] = snippet.option_dict[ALT]
326             rep1['info_image_path'] = os.path.join (self.global_options.info_images_dir, rep1['base'])
327             str += self.output[OUTPUTIMAGE] % rep1
328
329         str += self.output[OUTPUT] % rep
330         return str
331
332     def snippet_output (self, basename, snippet):
333         str = ''
334         base = basename
335         if DOCTITLE in snippet.option_dict:
336             doctitle = base + '.doctitle'
337             translated_doctitle = doctitle + self.document_language
338             if os.path.exists (translated_doctitle):
339                 str += '\n@lydoctitle %s\n\n' % open (translated_doctitle).read ()
340             elif os.path.exists (doctitle):
341                 str += '\n@lydoctitle %s\n\n' % open (doctitle).read ()
342         if TEXIDOC in snippet.option_dict:
343             texidoc = base + '.texidoc'
344             translated_texidoc = texidoc + self.document_language
345             if os.path.exists (translated_texidoc):
346                 str += '@include %(translated_texidoc)s\n\n' % vars ()
347             elif os.path.exists (texidoc):
348                 str += '@include %(texidoc)s\n\n' % vars ()
349         str += self.output_print_filename (basename, snippet)
350
351         substr = ''
352         rep = snippet.get_replacements ();
353         if VERBATIM in snippet.option_dict:
354             rep['version'] = ''
355             if ADDVERSION in snippet.option_dict:
356                 rep['version'] = self.output[ADDVERSION]
357             rep['verb'] = snippet.verb_ly ()
358             substr = self.output[VERBATIM] % rep
359         substr += self.output_info (basename, snippet)
360         if QUOTE in snippet.option_dict:
361             substr = self.output[QUOTE] % {'str': substr}
362         str += substr
363
364         # need par after image
365         str += '\n'
366
367         return str
368
369     def required_files (self, snippet, base, full, required_files):
370         return self.required_files_png (snippet, base, full, required_files)
371
372
373
374 BookBase.register_format (BookTexinfoOutputFormat ());