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