1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """User function definition conversion to LaTeX for the relax manual."""
24
25
26 from os import sep
27 from os.path import dirname
28 from re import search
29 from string import ascii_letters, ascii_lowercase, punctuation, whitespace
30 import sys
31
32
33 sys.path.append(sys.path[0])
34 sys.path[0] = '../..'
35
36
37 from graphics import fetch_icon
38 import user_functions
39 from user_functions.data import Uf_info; uf_info = Uf_info()
40 from user_functions.data import Uf_tables; uf_tables = Uf_tables()
41
42
43
44 user_functions.initialise()
45
46
48 - def __init__(self, file='docstring.tex'):
95
96
98 """Allow the function text to be broken nicely across lines.
99
100 The '\' character will be added later by the latex_special_chars() method.
101 """
102
103
104 text = text.replace("(", "(\linebreak[0]")
105
106
107 for char in ascii_letters:
108 text = text.replace(".%s" % char, ".\linebreak[0]%s" % char)
109
110
111 text = text.replace("=", "=\linebreak[0]")
112
113
114 text = text.replace("\linebreak", "linebreak")
115
116
117 return text
118
119
150
151
188
189
190
214
215
232
233
235 """Create the user function sectioning."""
236
237
238 self.file.write("\n\n")
239
240
241 self.file.write("\\pagebreak[4]\n")
242 self.file.write("\\rule{\columnwidth}{1pt}\n")
243
244
245 self.file.write("\\vspace{-20pt}\n")
246 self.uf_name_latex = self.uf_name
247
248
249 self.uf_name_latex = self.latex_special_chars(self.uf_name_latex)
250 self.uf_name_latex = self.word_formatting(self.uf_name_latex, bold=True)
251
252
253 self.uf_name_latex = self.uf_name_latex.replace('.', '\-.')
254 self.uf_name_latex = self.uf_name_latex.replace('\_', '\-\_')
255
256
257 self.file.write("\subsection{%s} \label{uf: %s}\n" % (self.uf_name_latex, self.uf_name))
258
259
260 if self.uf_class:
261 icon = fetch_icon(self.uf_class.gui_icon, size='128x128', format=None, sep='/', full_path=False)
262 if icon:
263 self.file.write("\includegraphics[bb=0 0 18 18]{%s} \hfill " % icon)
264 else:
265 self.file.write("\hfill ")
266
267
268 icon = fetch_icon(self.uf.gui_icon, size='128x128', format=None, sep='/', full_path=False)
269 if icon:
270 self.file.write("\includegraphics[bb=0 0 18 18]{%s}\n" % icon)
271 else:
272 self.file.write("\n")
273
274
275 self.file.write("\n")
276
277
279 """Insert index marks into the text, word by word.
280
281 @param index: The index of the word in the self.words data structure.
282 @type index: int
283 @keyword bold: A flag which if True will cause the index entries to be in bold font.
284 @type bold: bool
285 """
286
287
288 if bold:
289 end_string = '|textbf}'
290 else:
291 end_string = '}'
292
293
294 for i in range(len(self.entries)):
295
296 if index+2 < len(self.words) and self.entries[i][2] == 3 and search(self.entries[i][0], self.words[index] + ' ' + self.words[index+1] + ' ' + self.words[index+2]):
297 self.words[index] = self.words[index] + '\\index{' + self.entries[i][1] + end_string
298
299
300 elif index+1 < len(self.words) and self.entries[i][2] == 2 and search(self.entries[i][0], self.words[index] + ' ' + self.words[index+1]):
301 self.words[index] = self.words[index] + '\\index{' + self.entries[i][1] + end_string
302
303
304 elif self.entries[i][2] == 1 and search(self.entries[i][0], self.words[index]):
305 self.words[index] = self.words[index] + '\\index{' + self.entries[i][1] + end_string
306
307
309 """Function for returning a data structure containing all words which should be indexed."""
310
311
312 self.entries = []
313
314
315
316
317 self.entries.append(['AIC', 'model selection!AIC'])
318 self.entries.append(['AICc', 'model selection!AICc'])
319 self.entries.append(['angle', 'angles'])
320 self.entries.append(['anisotropic', 'diffusion!anisotropic'])
321 self.entries.append(['ANOVA', 'model selection!ANOVA'])
322 self.entries.append(['asymmetric', 'diffusion!ellipsoid (asymmetric)'])
323 self.entries.append(['axially symmetric', 'diffusion!spheroid (axially symmetric)'])
324
325 self.entries.append(['BIC', 'model selection!BIC'])
326 self.entries.append(['BFGS', 'optimisation!algorithm!BFGS'])
327 self.entries.append(['bond length', 'bond length'])
328 self.entries.append(['bootstrap', 'model selection!bootstrap'])
329 self.entries.append(['bound', 'parameter!bounds'])
330 self.entries.append(['Brownian', 'diffusion!Brownian'])
331 self.entries.append(['bzip2', 'compression!bzip2'])
332
333 self.entries.append(['cauchy', 'optimisation!algorithm!Cauchy point'])
334 self.entries.append(['CG-Steihaug', 'optimisation!algorithm!CG-Steihaug'])
335 self.entries.append(['chemical exchange', 'chemical exchange'])
336 self.entries.append(['chi-squared', 'chi-squared'])
337 self.entries.append(['compression', 'compression'])
338 self.entries.append(['conjugate gradient', 'optimisation!algorithm!conjugate gradient'])
339 self.entries.append(['constraint', 'constraint'])
340 self.entries.append(['copy', 'copy'])
341 self.entries.append(['correlation time', 'correlation time'])
342 self.entries.append(['cross-validation', 'model selection!cross-validation'])
343
344 self.entries.append(['dasha', 'software!Dasha'])
345 self.entries.append(['Dasha', 'software!Dasha'])
346 self.entries.append(['delete', 'delete'])
347 self.entries.append(['diffusion tensor', 'diffusion!tensor'])
348 self.entries.append(['display', 'display'])
349 self.entries.append(['dogleg', 'optimisation!algorithm!dogleg'])
350
351 self.entries.append(['eigenvalue', 'eigenvalues'])
352 self.entries.append(['elimination', 'model elimination'])
353 self.entries.append(['ellipsoid', 'diffusion!ellipsoid (asymmetric)'])
354 self.entries.append(['Euler angle', 'Euler angles'])
355 self.entries.append(['exact trust region', 'optimisation!algorithm!exact trust region'])
356
357 self.entries.append(['Fletcher-Reeves', 'optimisation!algorithm!Fletcher-Reeves'])
358 self.entries.append(['floating point', 'floating point number'])
359
360 self.entries.append(['grace', 'software!Grace'])
361 self.entries.append(['Grace', 'software!Grace'])
362 self.entries.append(['gzip', 'compression!gzip'])
363
364 self.entries.append(['Hestenes-Stiefel', 'optimisation!algorithm!Hestenes-Stiefel'])
365 self.entries.append(['hypothesis testing', 'model selection!hypothesis testing'])
366
367 self.entries.append(['isotropic', 'diffusion!sphere (isotropic)'])
368
369 self.entries.append(['Levenberg-Marquardt', 'optimisation!algorithm!Levenberg-Marquardt'])
370 self.entries.append(['limit', 'parameter!limit'])
371
372 self.entries.append(['map', 'map'])
373 self.entries.append(['method of [Mm]ultipliers', 'optimisation!constraint algorithm!Method of Multipliers'])
374 self.entries.append(['minimise', 'optimisation'])
375 self.entries.append(['minimisation', 'optimisation'])
376 self.entries.append(['model elimination', 'model elimination'])
377 self.entries.append(['modelfree4', 'software!Modelfree'])
378 self.entries.append(['Modelfree4', 'software!Modelfree'])
379 self.entries.append(['modelling', 'modelling'])
380 self.entries.append(['molecule', 'molecule'])
381 self.entries.append(['molmol', 'software!MOLMOL'])
382 self.entries.append(['Molmol', 'software!MOLMOL'])
383
384 self.entries.append(['opendx', 'software!OpenDX'])
385 self.entries.append(['OpenDX', 'software!OpenDX'])
386 self.entries.append(['optimise', 'optimise'])
387 self.entries.append(['order parameter', 'order parameter'])
388
389 self.entries.append(['newton', 'optimisation!algorithm!Newton'])
390 self.entries.append(['newton-CG', 'optimisation!algorithm!Newton conjugate gradient'])
391 self.entries.append(['NMR', 'NMR'])
392
393 self.entries.append(['PDB', 'PDB'])
394 self.entries.append(['Polak-Ribi.*re', 'optimisation!algorithm!Polak-Ribiere@Polak-Ribi\`ere'])
395 self.entries.append(['Polak-Ribi.*re +', 'optimisation!algorithm!Polak-Ribiere@Polak-Ribi\`ere +'])
396 self.entries.append(['plot', 'plot'])
397 self.entries.append(['python', 'Python'])
398
399 self.entries.append(['read', 'read'])
400 self.entries.append(['regular expression', 'regular expression'])
401 self.entries.append(['relaxation', 'relaxation'])
402 self.entries.append(['rotation', 'rotation'])
403
404 self.entries.append(['sequence', 'sequence'])
405 self.entries.append(['script', 'scripting!script file'])
406 self.entries.append(['scripting', 'scripting'])
407 self.entries.append(['simplex', 'optimisation!algorithm!Nelder-Mead simplex'])
408 self.entries.append(['sphere', 'diffusion!sphere (isotropic)'])
409 self.entries.append(['spheroid', 'diffusion!spheroid (axially symmetric)'])
410 self.entries.append(['sparky', 'software!Sparky'])
411 self.entries.append(['Sparky', 'software!Sparky'])
412 self.entries.append(['steepest descent', 'optimisation!algorithm!steepest descent'])
413
414 self.entries.append(['tar', 'tar'])
415
416 self.entries.append(['uncompressed', 'compression!uncompressed'])
417
418 self.entries.append(['write', 'write'])
419
420 self.entries.append(['xeasy', 'software!XEasy'])
421 self.entries.append(['Xeasy', 'software!XEasy'])
422 self.entries.append(['XEasy', 'software!XEasy'])
423
424
425 for i in range(len(self.entries)):
426
427 self.entries[i].append(len(self.entries[i][0].split(' ')))
428
429
430 if search(self.entries[i][0][0], ascii_lowercase):
431 self.entries[i][0] = '[' + self.entries[i][0][0].upper() + self.entries[i][0][0] + ']' + self.entries[i][0][1:]
432
433
434 self.entries[i][0] = '^' + self.entries[i][0]
435
436
437 self.entries[i].reverse()
438
439
440 self.entries.sort(reverse=1)
441 for i in range(len(self.entries)):
442 self.entries[i].reverse()
443
444
588
589
591 """Function for changing the quotes for LaTeX processing."""
592
593
594 new_string = ''
595 in_quote = 0
596
597
598 for i in range(len(string)):
599
600 if search('\'', string[i]):
601
602 if not in_quote and (i == 0 or not search('[a-z]', string[i-1])):
603 new_string = new_string + '`'
604 in_quote = 1
605 continue
606
607
608 else:
609 in_quote = 0
610
611
612 new_string = new_string + string[i]
613
614 return new_string
615
616
618 """Function for handling LaTeX special characters."""
619
620
621 string = string.replace('\\', 'This is a backslash to be replaced at the end of this functioN')
622
623
624 for char in "#$%&_{}":
625 string = string.replace(char, '\\'+char)
626
627
628 for char in "^~":
629 string = string.replace(char, '\\'+char+'{}')
630
631
632 string = string.replace('This is a backslash to be replaced at the end of this functioN', '$\\backslash$')
633
634
635 string = string.replace('linebreak[0]', '\linebreak[0]')
636
637
638 return string
639
640
642 """Function for placing quotes within the quote environment."""
643
644
645 elements = self.words[index].split("'")
646
647
648 if len(elements) == 3:
649 self.words[index] = elements[0] + '\quotecmd{' + elements[1] + '}' + elements[2]
650
651
652 elif len(elements) > 3:
653 sys.stderr.write('Unknown quote: ' + repr(self.words[index]))
654 sys.exit()
655
656
657 if len(elements) == 2:
658
659 if not self.in_quote and not search('[a-z]$', elements[0]):
660 self.words[index] = elements[0] + '\quotecmd{' + elements[1]
661 self.in_quote = 1
662
663
664 elif self.in_quote:
665 self.words[index] = elements[0] + '}' + elements[1]
666 self.in_quote = 0
667
668
670 """Only replace in safe places within the text."""
671
672
673
674
675 string = string.replace('0'+text, '0'+latex)
676 string = string.replace('1'+text, '1'+latex)
677 string = string.replace('2'+text, '2'+latex)
678 string = string.replace('3'+text, '3'+latex)
679 string = string.replace('4'+text, '4'+latex)
680 string = string.replace('5'+text, '5'+latex)
681 string = string.replace('6'+text, '6'+latex)
682 string = string.replace('7'+text, '7'+latex)
683 string = string.replace('8'+text, '8'+latex)
684 string = string.replace('9'+text, '9'+latex)
685
686
687 string = string.replace(' '+text+',', ' '+latex+',')
688 string = string.replace(' '+text+'.', ' '+latex+'.')
689 string = string.replace(' '+text+' ', ' '+latex+' ')
690 string = string.replace(' '+text+';', ' '+latex+';')
691 string = string.replace(' '+text+':', ' '+latex+':')
692
693
694 string = string.replace('['+text+']', '['+latex+']')
695 string = string.replace('['+text+' ', '['+latex+' ')
696 string = string.replace('['+text+',', '['+latex+',')
697 string = string.replace('['+text+';', '['+latex+';')
698 string = string.replace(' '+text+']', ' '+latex+']')
699
700
701 string = string.replace('('+text+')', '('+latex+')')
702 string = string.replace('('+text+' ', '('+latex+' ')
703 string = string.replace('('+text+',', '('+latex+',')
704 string = string.replace('('+text+';', '('+latex+';')
705 string = string.replace(' '+text+')', ' '+latex+')')
706
707
708 string = string.replace('{'+text+' ', '{'+latex+' ')
709 string = string.replace('{'+text+',', '{'+latex+',')
710 string = string.replace('{'+text+';', '{'+latex+';')
711 string = string.replace(' '+text+'\\', ' '+latex+'\\')
712
713
714 string = string.replace('`'+text+'\'', '`'+latex+'\'')
715 string = string.replace('`'+text+' ', '`'+latex+' ')
716 string = string.replace('`'+text+',', '`'+latex+',')
717 string = string.replace('`'+text+'.', '`'+latex+'.')
718 string = string.replace('`'+text+';', '`'+latex+';')
719 string = string.replace(' '+text+'\'', ' '+latex+'\'')
720
721
722 substring = string[-len(text)-1:].replace(' '+text, ' '+latex)
723 string = string[0:-len(text)-1] + substring
724
725 substring = string[-len(text)-1:].replace('.'+text, '.'+latex)
726 string = string[0:-len(text)-1] + substring
727
728 string = string.replace(' '+text+'\n', ' '+latex+'\n')
729 string = string.replace('.'+text+'\n', '.'+latex+'\n')
730
731
732 string = string.replace(' '+text+'\^', ' '+latex+'\^')
733 string = string.replace('('+text+'\^', '('+latex+'\^')
734 string = string.replace('\n'+text+'\^', '\n'+latex+'\^')
735
736
737 if search('^'+text+'['+punctuation+']', string) or search('^'+text+'['+whitespace+']', string) or search('\n'+text+'['+punctuation+']', string) or search('\n'+text+'['+whitespace+']', string):
738 string = string.replace(text+' ', latex+' ')
739 string = string.replace(text+',', latex+',')
740 string = string.replace(text+'.', latex+'.')
741 string = string.replace(text+';', latex+';')
742 string = string.replace(text+']', latex+']')
743 string = string.replace(text+')', latex+')')
744 string = string.replace(text+'^', latex+'^')
745 string = string.replace(text+'\\', latex+'\\')
746 string = string.replace(text+'\'', latex+'\'')
747 string = string.replace(text+'\n', latex+'\n')
748
749
750
751 return string
752
753
755 """Create a LaTeX file defining the relax language syntax for the listings package.
756
757 @param uf_names: The list of all user function names.
758 @type uf_names: list of str
759 """
760
761
762 file = open(self.path + sep + 'script_definition.tex', 'w')
763
764
765 py_keywords = [
766 'and',
767 'assert',
768 'break',
769 'continue',
770 'del',
771 'else',
772 'exec',
773 'global',
774 'help',
775 'if',
776 'in',
777 'is',
778 'for',
779 'not',
780 'or',
781 'pass',
782 'print',
783 'raise',
784 'return',
785 'while',
786 'yield'
787 ]
788 py_keywords2 = [
789 'as',
790 'from',
791 'import',
792 ]
793 py_keywords3 = [
794 'class',
795 'def',
796 ]
797
798
799 for lang in ['relax', 'relax_log']:
800
801 file.write("\lstdefinelanguage{%s}{\n" % lang)
802
803
804 file.write(" alsoletter={.>|},\n")
805
806
807 if lang == 'relax':
808 file.write(" morekeywords={")
809 for name in py_keywords:
810 file.write("%s," % name)
811 file.write("},\n")
812
813
814 if lang == 'relax':
815 file.write(" morekeywords=[2]{")
816 for name in py_keywords2:
817 file.write("%s," % name)
818 file.write("},\n")
819
820
821 if lang == 'relax':
822 file.write(" morekeywords=[3]{")
823 for name in py_keywords3:
824 file.write("%s," % name)
825 file.write("},\n")
826
827
828 file.write(" morekeywords=[4]{relax>,relax|},\n")
829
830
831 file.write(" morekeywords=[5]{")
832 for name in uf_names:
833 file.write("%s," % name)
834 file.write("},\n")
835
836
837 file.write(" moreprocnamekeys={def,class},\n")
838 file.write(" sensitive=true,\n")
839 file.write(" morecomment=[l]{\#},\n")
840 file.write(" morestring=[b]',\n")
841 file.write(" morestring=[b]\",\n")
842 file.write(" morestring=[b]\"\"\",\n")
843 file.write("}\n")
844
845
846 file.close()
847
848
850 """Determine if column wrapping should occur.
851
852 @param table: The table.
853 @type table: list of lists of str
854 @keyword max_char: The maximum number of characters a column is allowed before wrapping is applied.
855 @type max_char: int
856 @return: The list of flags for wrapping columns.
857 @rtype: list of bool
858 """
859
860
861 num_cols = len(table[0])
862 widths = [0] * num_cols
863 for i in range(len(table)):
864 for j in range(num_cols):
865
866 if len(table[i][j]) > widths[j]:
867 widths[j] = len(table[i][j])
868
869
870 wrap = []
871 for i in range(len(widths)):
872 if widths[i] > max_char:
873 wrap.append(True)
874 else:
875 wrap.append(False)
876
877
878 return wrap
879
880
914
915
917 """Format and write out an itemised list.
918
919 @param item_list: The list of items and description lists.
920 @type item_list: list of list of str
921 """
922
923
924 latex_lines = []
925 items = False
926 for i in range(len(item_list)):
927
928 item = item_list[i][0]
929 if item == None:
930 item = ''
931 if item != '':
932 items = True
933 item = self.latex_special_chars(item)
934 item = self.latex_formatting(item)
935 item = self.word_formatting(item)
936
937
938 desc = self.latex_special_chars(item_list[i][1])
939 desc = self.latex_formatting(desc)
940 desc = self.word_formatting(desc)
941
942
943 if item != '':
944 latex_lines.append(" \\item[%s --] %s\n" % (item, desc))
945 else:
946 latex_lines.append(" \\item[]%s\n" % desc)
947
948
949 if not items:
950 self.file.write("\\begin{itemize}\n")
951 else:
952 self.file.write("\\begin{description}\n")
953
954
955 for line in latex_lines:
956 self.file.write(line)
957
958
959 if not items:
960 self.file.write("\\end{itemize}\n\n")
961 else:
962 self.file.write("\\end{description}\n\n")
963
964
966 """Format and write out a list.
967
968 @param list: The list.
969 @type list: list of str
970 """
971
972
973 self.file.write("\\begin{itemize}\n")
974
975
976 for item in list:
977
978 item = self.latex_special_chars(item)
979 item = self.latex_formatting(item)
980 item = self.word_formatting(item)
981
982
983 self.file.write(" \\item[]%s\n" % item)
984
985
986 self.file.write("\\end{itemize}\n\n")
987
988
1003
1004
1006 """Format and write out the prompt UI examples.
1007
1008 @param list: The list of prompt UI examples.
1009 @type list: list of str
1010 """
1011
1012
1013 for text in list:
1014
1015 self.file.write("\\begin{lstlisting}[numbers=none]\n%s\n\\end{lstlisting}\n\n" % text)
1016
1017
1018 self.file.write("\n")
1019
1020
1022 """Format and write out a table.
1023
1024 @param label: The unique table label.
1025 @type label: list of lists of str
1026 """
1027
1028
1029 table = uf_tables.get_table(label)
1030
1031
1032 self.file.write("Please see Table~\\ref{%s} on page~\\pageref{%s}.\n\n" % (label, label))
1033
1034
1035 if label in self.uf_table_labels:
1036 return
1037 else:
1038 self.uf_table_labels.append(label)
1039
1040
1041 col_wrap = self.tabular_wrapping(table.cells)
1042 wrap = sum(col_wrap)
1043
1044
1045 num_rows = len(table.cells)
1046 num_cols = len(table.headings)
1047
1048
1049 if table.longtable:
1050
1051 self.file.write("\\onecolumn\n")
1052 self.file.write("\\begin{scriptsize}\n")
1053 self.file.write("\\begin{center}\n")
1054 self.file.write("\\begin{longtable}{%s}\n" % ("l"*num_cols))
1055 else:
1056
1057 self.file.write("\\begin{table*}\n")
1058 self.file.write("\\begin{scriptsize}\n")
1059 self.file.write("\\begin{center}\n")
1060
1061
1062 self.file.write("\\caption[%s]{%s}\n" % (table.caption_short, table.caption))
1063
1064
1065 if table.longtable:
1066
1067 self.file.write("\\\\\n")
1068 self.file.write("\\toprule\n")
1069 else:
1070
1071 if wrap:
1072 self.file.write("\\begin{tabularx}{\\textwidth}{")
1073 else:
1074 self.file.write("\\begin{tabular}{")
1075 for i in range(num_cols):
1076 if col_wrap[i]:
1077 text = "X"
1078 else:
1079 text = "l"
1080 self.file.write(text)
1081 self.file.write("}\n")
1082 self.file.write("\\\\[-5pt]\n")
1083 self.file.write("\\toprule\n")
1084
1085
1086 for j in range(num_cols):
1087
1088 if j > 0:
1089 self.file.write(' & ')
1090
1091
1092 cell = table.headings[j]
1093 cell = self.latex_special_chars(cell)
1094 cell = self.latex_formatting(cell)
1095
1096
1097 self.file.write(cell)
1098
1099
1100 self.file.write(" \\\\\n")
1101
1102
1103 if table.longtable:
1104 self.file.write("\\midrule\n")
1105 self.file.write("\\endhead\n\n")
1106 self.file.write("\\bottomrule\n")
1107 self.file.write("\\endfoot\n")
1108 else:
1109
1110 self.file.write("\\midrule\n")
1111
1112
1113 if table.longtable:
1114 self.file.write("\\label{%s}\n" % label)
1115
1116
1117 for i in range(num_rows):
1118
1119 for j in range(num_cols):
1120
1121 if j > 0:
1122 self.file.write(' & ')
1123
1124
1125 cell = table.cells[i][j]
1126 cell = self.latex_special_chars(cell)
1127 cell = self.latex_formatting(cell)
1128
1129
1130 self.file.write(cell)
1131
1132
1133 self.file.write(" \\\\\n")
1134
1135
1136 if table.longtable:
1137 self.file.write("\\end{longtable}\n")
1138 self.file.write("\\end{center}\n")
1139 self.file.write("\\end{scriptsize}\n")
1140 self.file.write("\\twocolumn\n")
1141 else:
1142 self.file.write("\\bottomrule\n")
1143 self.file.write("\\\\[-5pt]\n")
1144 self.file.write("\\label{%s}\n" % label)
1145 if wrap:
1146 self.file.write("\\end{tabularx}\n")
1147 else:
1148 self.file.write("\\end{tabular}\n")
1149 self.file.write("\\end{center}\n")
1150 self.file.write("\\end{scriptsize}\n")
1151 self.file.write("\\end{table*}\n")
1152
1153
1154 self.table_count += 1
1155 self.uf_table_count += 1
1156
1157
1158 self.file.write("\n\n")
1159
1160
1162 """Format and write out the verbatim text.
1163
1164 @param text: The text to write out in a LaTeX verbatim environment.
1165 @type text: str
1166 """
1167
1168
1169 self.file.write("{\\footnotesize \\begin{verbatim}\n")
1170 self.file.write(text)
1171 self.file.write("\n\\end{verbatim}}\n\n")
1172