]> git.lyx.org Git - lyx.git/blob - lib/scripts/layout2layout.py
DocBook: add new layout parameter DocBookWrapperMergeWithPrevious.
[lyx.git] / lib / scripts / layout2layout.py
1 # -*- coding: utf-8 -*-
2
3 # file layout2layout.py
4 # This file is part of LyX, the document processor.
5 # Licence details can be found in the file COPYING.
6
7 # author Georg Baum
8
9 # Full author contact details are available in file CREDITS
10
11 # This script will update a .layout file to current format
12
13 # The latest layout format is also defined in src/TextClass.cpp
14 currentFormat = 83
15
16
17 # Incremented to format 4, 6 April 2007, lasgouttes
18 # Introduction of generic "Provides" declaration
19
20 # Incremented to format 5, 22 August 2007 by vermeer
21 # InsetLayout material
22
23 # Incremented to format 6, 7 January 2008 by spitz
24 # Requires tag added to layout files
25
26 # Incremented to format 7, 24 March 2008 by rgh
27 # AddToPreamble tag added to layout files
28
29 # Incremented to format 8, 25 July 2008 by rgh
30 # UseModule tag added to layout files
31 # CopyStyle added to InsetLayout
32
33 # Incremented to format 9, 5 October 2008 by rgh
34 # ForcePlain and CustomPars tags added to InsetLayout
35
36 # Incremented to format 10, 6 October 2008 by rgh
37 # Change format of counters
38
39 # Incremented to format 11, 14 October 2008 by rgh
40 # Add ProvidesModule, ExcludesModule tags
41
42 # Incremented to format 12, 10 January 2009 by gb
43 # Add I18NPreamble tag
44
45 # Incremented to format 13, 5 February 2009 by rgh
46 # Add InToc tag for InsetLayout
47
48 # Incremented to format 14, 14 February 2009 by gb
49 # Rename I18NPreamble to BabelPreamble and add LangPreamble
50
51 # Incremented to format 15, 28 May 2009 by lasgouttes
52 # Add new tag OutputFormat; modules can be conditioned on feature
53 # "from->to".
54
55 # Incremented to format 16, 5 June 2009 by rgh
56 # Add new tags for Text Class:
57 #   HTMLPreamble, HTMLAddToPreamble
58 # For Layout:
59 #   HTMLTag, HTMLAttr, HTMLLabel, HTMLLabelAttr, HTMLItem, HTMLItemAttr
60 #   HTMLStyle, and HTMLPreamble
61 # For InsetLayout:
62 #   HTMLTag, HTMLAttr, HTMLStyle, and HTMLPreamble
63 # For Floats:
64 #   HTMLType, HTMLClass, HTMLStyle
65
66 # Incremented to format 17, 12 August 2009 by rgh
67 # Add IfStyle and IfCounter tags for layout.
68
69 # Incremented to format 18, 27 October 2009 by rgh
70 # Added some new tags for HTML output.
71
72 # Incremented to format 19, 17 November 2009 by rgh
73 # Added InPreamble tag.
74
75 # Incremented to format 20, 17 December 2009 by rgh
76 # Added ContentAsLabel tag.
77
78 # Incremented to format 21, 12 January 2010 by rgh
79 # Added HTMLTocLayout and HTMLTitle tags.
80
81 # Incremented to format 22, 20 January 2010 by rgh
82 # Added HTMLFormat tag to Counters.
83
84 # Incremented to format 23, 13 February 2010 by spitz
85 # Added Spellcheck tag.
86
87 # Incremented to format 24, 5 March 2010 by rgh
88 # Changed LaTeXBuiltin tag to NeedsFloatPkg and
89 # added new tag ListCommand.
90
91 # Incremented to format 25, 12 March 2010 by rgh
92 # Added RefPrefix tag for layouts and floats.
93
94 # Incremented to format 26, 29 March 2010 by rgh
95 # Added CiteFormat.
96
97 # Incremented to format 27, 4 June 2010 by rgh
98 # Added RequiredArgs tag.
99
100 # Incremented to format 28, 6 August 2010 by lasgouttes
101 # Added ParbreakIsNewline tag for Layout and InsetLayout.
102
103 # Incremented to format 29, 10 August 2010 by rgh
104 # Changed Custom:Style, CharStyle:Style, and Element:Style
105 # uniformly to Flex:Style.
106
107 # Incremented to format 30, 13 August 2010 by rgh
108 # Introduced ResetsFont tag for InsetLayout.
109
110 # Incremented to format 31, 12 January 2011 by rgh
111 # Introducted NoCounter tag.
112
113 # Incremented to format 32, 30 January 2011 by forenr
114 # Added Display tag for InsetLayout
115
116 # Incremented to format 33, 2 February 2011 by rgh
117 # Changed NeedsFloatPkg to UsesFloatPkg
118
119 # Incremented to format 34, 28 March 2011 by rgh
120 # Remove obsolete Fill_(Top|Bottom) tags
121
122 # Incremented to format 35, 28 March 2011 by rgh
123 # Try to add "Flex:" to any flex insets that don't have it.
124
125 # Incremented to format 36, 7 December 2011, by rgh
126 # Added HTMLStyles and AddToHTMLStyles tags.
127
128 # Incremented to format 37, 29 February 2012 by jrioux
129 # Implement the citation engine machinery in layouts.
130 # Change CiteFormat to CiteFormat (default|authoryear|numerical).
131
132 # Incremented to format 38, 08 April 2012 by gb
133 # Introduce LangPreamble and BabelPreamble for InsetLayout.
134
135 # Incremented to format 39, 15 April 2012 by sanda
136 # Introduce styling of branches via "InsetLayout Branch:".
137
138 # Incremented to format 40, 10 October 2012 by rgh
139 # Re-do layout names for layout categories
140
141 # Incremented to format 41, 20 November 2012 by spitz
142 # New Argument syntax
143
144 # Incremented to format 42, 22 December 2012 by spitz
145 # New Style tag "ItemCommand"
146
147 # Incremented to format 43, 30 December 2012 by spitz
148 # Extended InsetCaption format
149
150 # Incremented to format 44, 9 February 2013 by rgh
151 # Remove COUNTER label style; rename as STATIC
152 # Rename TOP_ENVIRONMENT to ABOVE and CENTERED_TOP_ENVIRONMENT to CENTERED
153
154 # Incremented to format 45, 12 February 2013 by rgh
155 # New Tag "NoInsetLayout"
156
157 # Incremented to format 46, 15 May 2013 by gb
158 # New Tag "ForceLocal"
159
160 # Incremented to format 47, 23 May 2013 by rgh
161 # Add PackageOptions tag
162
163 # Incremented to format 48, 31 May 2013 by rgh
164 # Add InitialValue tag for counters
165
166 # Incremented to format 49, 10 Feb 2014 by gb
167 # Change default of "ResetsFont" tag to false
168
169 # Incremented to format 50, 9 May 2014 by forenr
170 # Removal of "Separator" layouts
171
172 # Incremented to format 51, 29 May 2014 by spitz
173 # New Style tag "ToggleIndent"
174
175 # Incremented to format 52, 1 December 2014 by spitz
176 # New InsetLayout tag "ForceOwnlines"
177
178 # Incremented to format 53, 7 December 2014 by spitz
179 # New InsetLayout tag "ObsoletedBy"
180
181 # Incremented to format 54, 11 Jan 2014 by gb
182 # New InsetLayout tag "FixedWidthPreambleEncoding"
183
184 # Incremented to format 55, 20 April 2015 by spitz
185 # New InsetLayout and Layout tags "PassThruChars"
186
187 # Incremented to format 56, 20 May 2015 by spitz
188 # New Float tags "AllowedPlacement", "AllowsWide", "AllowsSideways"
189
190 # Incremented to format 57, 30 May 2015 by spitz
191 # New Layout tag "ParagraphGroup"
192
193 # Incremented to format 58, 5 December 2015, by rgh
194 # New Layout tag "ProvideStyle"
195 # Change "IfStyle" to "ModifyStyle"
196
197 # Incremented to format 59, 22 November 2015 by gm
198 # New Tag "OutlinerName"
199 # New Layout tags "AddToToc", "IsTocCaption"
200 # New Layout argument tag "IsTocCaption"
201
202 # Incremented to format 60, 25 March 2016 by lasgouttes
203 # Rename caption subtype LongTableNoNumber to Unnumbered
204
205 # Incremented to format 61, 14 October 2016 by spitz
206 # New Layout tags "ResumeCounter", "StepMasterCounter"
207
208 # Incremented to format 62, 21 October 2016 by spitz
209 # New Layout argument tag "PassThru"
210
211 # Incremented to format 63, 7 January 2017 by spitz
212 # - New textclass tags CiteFramework, MaxCiteNames (for cite engines)
213 # - Extended InsetCite syntax.
214
215 # Incremented to format 64, 30 August 2017 by rgh
216 # Strip leading and trailing spaces from LabelString,
217 # LabelStringAppendix, and EndLabelString, and LabelCounter,
218 # to conform to what we used to do.
219
220 # Incremented to format 65, 16 October 2017 by spitz
221 # Color collapsable -> collapsible
222
223 # Incremented to format 66, 28 December 2017 by spitz
224 # New Layout tags "AutoNests ... EndAutoNests" and
225 # "IsAutoNestedBy ... EndIsAutoNestedBy"
226
227 # Incremented to format 67, 14 April 2018 by spitz
228 # New Layout tag "NeedsCProtect"
229
230 # Incremented to format 68, 21 May 2018 by spitz
231 # New Layout tag "AddToCiteEngine"
232
233 # Incremented to format 69, 16 August 2018 by spitz
234 # New argument type "listpreamble"
235
236 # Incremented to format 70, 5 June 2018 by rkh
237 # New InsetLayout tag EditExternal
238
239 # Incremented to format 71, 12 March 2019 by spitz
240 # New [Inset]Layout tag NeedMBoxProtect
241
242 # Incremented to format 72, 26 March 2019 by spitz
243 # New TextClass tag TableStyle
244
245 # Incremented to format 73, 18 April 2019 by spitz
246 # New InsetLayout tag MenuString
247
248 # Incremented to format 74, 18 April 2019 by spitz
249 # New InsetLayout and Argument tag NewlineCmd
250
251 # Incremented to format 75, 2 June 2019 by spitz
252 # New Argument tags FreeSpacing, InsertOnNewline
253 # New InsetLayout tag ParbreakIgnored
254
255 # Incremented to format 76, 8 July 2019 by spitz
256 # New textclass tag BibInToc
257
258 # Incremented to format 77, 6 August 2019 by spitz
259 # New textclass tag PageSize (= default page size) 
260 # and textclass option PageSize (= list of available page sizes)
261
262 # Incremented to format 78, 6 August 2019 by spitz
263 # New textclass tag FontsizeFormat
264
265 # Incremented to format 79, 7 August 2019 by spitz
266 # New textclass tag PagesizeFormat
267
268 # Incremented to format 80, 12 August 2019 by spitz
269 # New float option Requires
270
271 # Incremented to format 81, 12 August 2019 by rikiheck
272 # New tag GuiName for counters
273
274 # Incremented to format 82, 4 June 2017 by dourouc05
275 # - Add new tags for Layout:
276 #   DocBookTag, DocBookAttr, DocBookInInfo,
277 #   DocBookWrapperTag, DocBookWrapperAttr,
278 #   DocBookItemWrapperTag, DocBookItemWrapperAttr,
279 #   DocBookItemTag, DocBookItemAttr,
280 #   DocBookLabelTag, DocBookLabelAttr
281 # - Removed tag Header from ClassOptionsClassOptions
282 # - Removed tag Element for flex insets
283
284 # Incremented to format 83, 2 August 2020 by dourouc05
285 # New tag DocBookWrapperMergeWithPrevious
286
287 # Do not forget to document format change in Customization
288 # Manual (section "Declaring a new text class").
289
290 # You might also want to consider running the
291 # development/tools/updatelayouts.py script to update all
292 # layout files to the new format.
293
294
295 import os, re, sys
296 import argparse
297
298 # Provide support for both python 2 and 3
299 # (copied from lyx2lyx)
300 PY2 = sys.version_info[0] == 2
301 if PY2:
302     # argparse returns strings in the commandline encoding, we need to convert.
303     # sys.getdefaultencoding() would not always be correct, see
304     # http://legacy.python.org/dev/peps/pep-0383/
305     def cmd_arg(arg):
306         return arg.decode(sys.getfilesystemencoding())
307 else:
308     cmd_arg = str
309 # End of code to support for both python 2 and 3
310
311
312 def error(message):
313     sys.stderr.write(message + '\n')
314     sys.exit(1)
315
316
317 def trim_bom(line):
318     " Remove byte order mark."
319     if line[0:3] == "\357\273\277":
320         return line[3:]
321     else:
322         return line
323
324
325 def read(source):
326     " Read input file and strip lineendings."
327     lines = source.read().splitlines() or ['']
328     lines[0] = trim_bom(lines[0])
329     return lines
330
331
332 def write(output, lines):
333     " Write output file with native lineendings."
334     output.write(os.linesep.encode('ascii').join(lines)
335                  + os.linesep.encode('ascii'))
336
337
338 # Concatenates old and new in an intelligent way:
339 # If old is wrapped in ", they are stripped. The result is wrapped in ".
340 def concatenate_label(old, new):
341     # Don't use strip as long as we support python 1.5.2
342     if old[0] == b'"':
343         return old[0:-1] + new + b'"'
344     else:
345         return b'"' + old + new + b'"'
346
347 # appends a string to a list unless it's already there
348 def addstring(s, l):
349     if l.count(s) > 0:
350         return
351     l.append(s)
352
353
354 def convert(lines, end_format):
355     " Convert to new format."
356     re_Comment = re.compile(b'^(\\s*)#')
357     re_Counter = re.compile(b'\\s*Counter\\s*', re.IGNORECASE)
358     re_Name = re.compile(b'\\s*Name\\s+(\\S+)\\s*', re.IGNORECASE)
359     re_UseMod = re.compile(b'^\\s*UseModule\\s+(.*)', re.IGNORECASE)
360     re_Empty = re.compile(b'^(\\s*)$')
361     re_Format = re.compile(b'^(\\s*)(Format)(\\s+)(\\S+)', re.IGNORECASE)
362     re_Preamble = re.compile(b'^(\\s*)Preamble', re.IGNORECASE)
363     re_EndPreamble = re.compile(b'^(\\s*)EndPreamble', re.IGNORECASE)
364     re_LangPreamble = re.compile(b'^(\\s*)LangPreamble', re.IGNORECASE)
365     re_EndLangPreamble = re.compile(b'^(\\s*)EndLangPreamble', re.IGNORECASE)
366     re_BabelPreamble = re.compile(b'^(\\s*)BabelPreamble', re.IGNORECASE)
367     re_EndBabelPreamble = re.compile(b'^(\\s*)EndBabelPreamble', re.IGNORECASE)
368     re_MaxCounter = re.compile(b'^(\\s*)(MaxCounter)(\\s+)(\\S+)', re.IGNORECASE)
369     re_LabelType = re.compile(b'^(\\s*)(LabelType)(\\s+)(\\S+)', re.IGNORECASE)
370     re_LabelString = re.compile(b'^(\\s*)(LabelString)(\\s+)(("[^"]+")|(\\S+))', re.IGNORECASE)
371     re_LabelStringAppendix = re.compile(b'^(\\s*)(LabelStringAppendix)(\\s+)(("[^"]+")|(\\S+))', re.IGNORECASE)
372     re_LatexType = re.compile(b'^(\\s*)(LatexType)(\\s+)(\\S+)', re.IGNORECASE)
373     re_Style = re.compile(b'^(\\s*)(Style)(\\s+)(\\S+)', re.IGNORECASE)
374     re_IfStyle = re.compile(b'^(\\s*)IfStyle(\\s+\\S+)', re.IGNORECASE)
375     re_CopyStyle = re.compile(b'^(\\s*)(CopyStyle)(\\s+)(\\S+)', re.IGNORECASE)
376     re_NoStyle = re.compile(b'^(\\s*)(NoStyle)(\\s+)(\\S+)', re.IGNORECASE)
377     re_End = re.compile(b'^(\\s*)(End)(\\s*)$', re.IGNORECASE)
378     re_Provides = re.compile(b'^(\\s*)Provides(\\S+)(\\s+)(\\S+)', re.IGNORECASE)
379     re_CharStyle = re.compile(b'^(\\s*)CharStyle(\\s+)(\\S+)$', re.IGNORECASE)
380     re_CiteFormat = re.compile(b'^(\\s*)(CiteFormat)(?:(\\s*)()|(\\s+)(default|authoryear|numerical))', re.IGNORECASE)
381     re_AMSMaths = re.compile(b'^\\s*Input ams(?:math|def)s.inc\\s*')
382     re_AMSMathsPlain = re.compile(b'^\\s*Input amsmaths-plain.inc\\s*')
383     re_AMSMathsSeq = re.compile(b'^\\s*Input amsmaths-seq.inc\\s*')
384     re_TocLevel = re.compile(b'^(\\s*)(TocLevel)(\\s+)(\\S+)', re.IGNORECASE)
385     re_I18nPreamble = re.compile(b'^(\\s*)I18nPreamble', re.IGNORECASE)
386     re_EndI18nPreamble = re.compile(b'^(\\s*)EndI18nPreamble', re.IGNORECASE)
387     re_Float = re.compile(b'^\\s*Float\\s*$', re.IGNORECASE)
388     re_Type = re.compile(b'\\s*Type\\s+(\\w+)', re.IGNORECASE)
389     re_Builtin = re.compile(b'^(\\s*)LaTeXBuiltin\\s+(\\w*)', re.IGNORECASE)
390     re_True = re.compile(b'^\\s*(?:true|1)\\s*$', re.IGNORECASE)
391     re_InsetLayout = re.compile(b'^\\s*InsetLayout\\s+(?:Custom|CharStyle|Element):(\\S+)\\s*$', re.IGNORECASE)
392     re_ResetsFont = re.compile(b'^(\\s*)ResetsFont(\\s+)(\\S+)$', re.IGNORECASE)
393     # with quotes
394     re_QInsetLayout = re.compile(b'^\\s*InsetLayout\\s+"(?:Custom|CharStyle|Element):([^"]+)"\\s*$', re.IGNORECASE)
395     re_InsetLayout_CopyStyle = re.compile(b'^\\s*CopyStyle\\s+(?:Custom|CharStyle|Element):(\\S+)\\s*$', re.IGNORECASE)
396     re_QInsetLayout_CopyStyle = re.compile(b'^\\s*CopyStyle\\s+"(?:Custom|CharStyle|Element):([^"]+)"\\s*$', re.IGNORECASE)
397     re_NeedsFloatPkg = re.compile(b'^(\\s*)NeedsFloatPkg\\s+(\\w+)\\s*$', re.IGNORECASE)
398     re_Fill = re.compile(b'^\\s*Fill_(?:Top|Bottom).*$', re.IGNORECASE)
399     re_InsetLayout2 = re.compile(b'^\\s*InsetLayout\\s+(\\S+)\\s*$', re.IGNORECASE)
400     # with quotes
401     re_QInsetLayout2 = re.compile(b'^\\s*InsetLayout\\s+"([^"]+)"\\s*$', re.IGNORECASE)
402     re_IsFlex = re.compile(b'\\s*LyXType.*$', re.IGNORECASE)
403     re_CopyStyle2 = re.compile(b'(\\s*CopyStyle\\s+)"?([^"]+)"?\\s*$')
404     re_Separator = re.compile(b'^(?:(-*)|(\\s*))(Separator|EndOfSlide)(?:(-*)|(\\s*))$', re.IGNORECASE)
405     # for categories
406     re_Declaration = re.compile(b'^#\\s*\\Declare\\w+Class.*$')
407     re_ExtractCategory = re.compile(b'^(#\\s*\\Declare\\w+Class(?:\\[[^]]*?\\])?){([^(]+?)\\s+\\(([^)]+?)\\)\\s*}\\s*$')
408     ConvDict = {"article": "Articles", "book" : "Books", "letter" : "Letters", "report": "Reports",
409                 "presentation" : "Presentations", "curriculum vitae" : "Curricula Vitae", "handout" : "Handouts"}
410     # Arguments
411     re_OptArgs = re.compile(b'^(\\s*)OptionalArgs(\\s+)(\\d+)\\D*$', re.IGNORECASE)
412     re_ReqArgs = re.compile(b'^(\\s*)RequiredArgs(\\s+)(\\d+)\\D*$', re.IGNORECASE)
413
414     # various changes associated with changing how chapters are handled
415     re_LabelTypeIsCounter = re.compile(b'^(\\s*)LabelType(\\s*)Counter\\s*$', re.IGNORECASE)
416     re_TopEnvironment = re.compile(b'^(\\s*)LabelType(\\s+)Top_Environment\\s*$', re.IGNORECASE)
417     re_CenteredEnvironment = re.compile(b'^(\\s*)LabelType(\\s+)Centered_Top_Environment\\s*$', re.IGNORECASE)
418     re_ChapterStyle = re.compile(b'^\\s*Style\\s+Chapter\\s*$', re.IGNORECASE)
419     re_InsetLayout_CaptionLTNN = re.compile(b'^(\\s*InsetLayout\\s+)(Caption:LongTableNonumber)', re.IGNORECASE)
420     # for format 64
421     re_trimLabelString = re.compile(b'^(\\s*LabelString\s+)"\\s*(.*?)\\s*"\\s*$')
422     re_trimLabelStringAppendix  = re.compile(b'^(\\s*LabelStringAppendix\s+)"\\s*(.*?)\\s*"\\s*$')
423     re_trimEndLabelString = re.compile(b'^(\\s*EndLabelString\s+)"\\s*(.*?)\\s*"\\s*$')
424     re_trimLabelCounter = re.compile(b'^(\\s*LabelCounter\s+)"\\s*(.*?)\\s*"\\s*$')
425
426
427     # counters for sectioning styles (hardcoded in 1.3)
428     counters = {b"part"          : b"\\Roman{part}",
429                 b"chapter"       : b"\\arabic{chapter}",
430                 b"section"       : b"\\arabic{section}",
431                 b"subsection"    : b"\\arabic{section}.\\arabic{subsection}",
432                 b"subsubsection" : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}",
433                 b"paragraph"     : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}.\\arabic{paragraph}",
434                 b"subparagraph"  : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}.\\arabic{paragraph}.\\arabic{subparagraph}"}
435
436     # counters for sectioning styles in appendix (hardcoded in 1.3)
437     appendixcounters = {b"chapter"       : b"\\Alph{chapter}",
438                         b"section"       : b"\\Alph{section}",
439                         b"subsection"    : b"\\arabic{section}.\\arabic{subsection}",
440                         b"subsubsection" : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}",
441                         b"paragraph"     : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}.\\arabic{paragraph}",
442                         b"subparagraph"  : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}.\\arabic{paragraph}.\\arabic{subparagraph}"}
443
444     # Value of TocLevel for sectioning styles
445     toclevels = {b"part"          : -1,
446                  b"chapter"       : 0,
447                  b"section"       : 1,
448                  b"subsection"    : 2,
449                  b"subsubsection" : 3,
450                  b"paragraph"     : 4,
451                  b"subparagraph"  : 5}
452
453     i = 0
454     only_comment = 1
455     counter = b""
456     toclevel = b""
457     label = b""
458     labelstring = b""
459     labelstringappendix = b""
460     space1 = b""
461     labelstring_line = -1
462     labelstringappendix_line = -1
463     labeltype_line = -1
464     latextype = b""
465     latextype_line = -1
466     style = b""
467     maxcounter = 0
468     format = 1
469     formatline = 0
470     usemodules = []
471     flexstyles = []
472     opts = 0
473     reqs = 0
474     inchapter = False
475     isflexlayout = False         # only used for 48 -> 49
476     # Whether a style is inherited (works only for CopyStyle currently,
477     # not for true inherited styles, see bug 8920
478     inherited = False        # only used for 48 -> 49
479     resetsfont_found = False # only used for 48 -> 49
480
481     while i < len(lines):
482         # Skip comments and empty lines
483         if (re_Comment.match(lines[i]) or re_Empty.match(lines[i])):
484           # We need to deal with this conversion here, because it happens
485           # inside the initial comment block.
486           if only_comment and format == 39:
487               match = re_ExtractCategory.match(lines[i])
488               if match:
489                   lpre = match.group(1)
490                   lcat = match.group(2)
491                   lnam = match.group(3)
492                   if lcat in ConvDict:
493                       lcat = ConvDict[lcat]
494                   lines[i] = lpre + b"{" + lnam + b"}"
495                   lines.insert(i+1, b"#  \\DeclareCategory{" + lcat + b"}")
496                   i += 1
497           i += 1
498           continue
499
500         # insert file format if not already there
501         if only_comment:
502             match = re_Format.match(lines[i])
503             if match:
504                 formatline = i
505                 format = int(match.group(4))
506                 if 1 < format < end_format:
507                     lines[i] = b"Format %d" % (format + 1)
508                     only_comment = 0
509                 elif format == end_format:
510                     # nothing to do
511                     return format
512                 else:
513                     error('Cannot convert file format %s to %s' % (format, end_format))
514             else:
515                 lines.insert(i, b"Format 2")
516                 only_comment = 0
517                 continue
518
519         # Don't get confused by LaTeX code
520         if re_Preamble.match(lines[i]):
521             i += 1
522             while i < len(lines) and not re_EndPreamble.match(lines[i]):
523                 i += 1
524             continue
525         if re_LangPreamble.match(lines[i]):
526             i += 1
527             while i < len(lines) and not re_EndLangPreamble.match(lines[i]):
528                 i += 1
529             continue
530         if re_BabelPreamble.match(lines[i]):
531             i += 1
532             while i < len(lines) and not re_EndBabelPreamble.match(lines[i]):
533                 i += 1
534             continue
535
536         if format == 81:
537             match = re.compile(b'^(\\s*Header\\s+)("?\\w+"?)', re.IGNORECASE).match(lines[i])
538             if match:
539                 del lines[i]
540                 continue
541
542             match = re.compile(b'(\\s*LyXType\\s+)(\\w+)(\\s*Element\\s+)', re.IGNORECASE).match(lines[i])
543             if match:
544                 del lines[i]
545                 continue
546
547             i += 1
548             continue
549
550         if 65 <= format <= 80:
551             # nothing to do.
552             i += 1
553             continue
554
555         if format == 64:
556             match = re.compile(b'(\\s*Color\\s+)(\\w+)', re.IGNORECASE).match(lines[i])
557             if not match:
558                 i += 1
559                 continue
560             col  = match.group(2)
561             if col == "collapsable":
562                 lines[i] = match.group(1) + "collapsible"
563             i += 1
564             continue
565
566         if format == 63:
567             for r in (re_trimLabelString, re_trimLabelStringAppendix,\
568               re_trimEndLabelString, re_trimLabelCounter):
569                 m = r.match(lines[i])
570                 if m:
571                     lines[i] = m.group(1) + b'"' + m.group(2) + b'"'
572             i += 1
573             continue
574
575         if 60 <= format <= 62:
576             # nothing to do.
577             i += 1
578             continue
579
580         if format == 59:
581             match = re_InsetLayout_CaptionLTNN.match(lines[i])
582             if not match:
583                 i += 1
584                 continue
585             # '^(\s*InsetLayout\s+)(Caption:LongTableNonumber)'
586             lead  = match.group(1)
587             lines[i] = lead + b"Caption:Unnumbered"
588             i += 1
589             continue
590
591         if format == 58:
592             # nothing to do.
593             i += 1
594             continue
595
596         if format == 57:
597             match = re_IfStyle.match(lines[i])
598             if not match:
599                 i += 1
600                 continue
601             # b'^(\\s*)IfStyle(\\s+\\S+)
602             lead  = match.group(1)
603             trail = match.group(2)
604             lines[i] = lead + b"ModifyStyle" + trail
605             i += 1
606             continue
607
608         if 50 <= format <= 56:
609             # nothing to do.
610             i += 1
611             continue
612
613         if format == 49:
614             separator = []
615
616             # delete separator styles
617             match = re_Style.match(lines[i])
618             if match:
619                 style = match.group(4).lower()
620                 if re_Separator.match(style):
621                     del lines[i]
622                     while i < len(lines) and not re_End.match(lines[i]):
623                         separator.append(lines[i])
624                         del lines[i]
625                     if i == len(lines):
626                         error('Incomplete separator style.')
627                     else:
628                         del lines[i]
629                         continue
630
631             # delete undefinition of separator styles
632             match = re_NoStyle.match(lines[i])
633             if match:
634                 style = match.group(4).lower()
635                 if re_Separator.match(style):
636                     del lines[i]
637                     continue
638
639             # replace the CopyStyle statement with the definition of the real
640             # style. This may result in duplicate statements, but that is OK
641             # since the second one will overwrite the first one.
642             match = re_CopyStyle.match(lines[i])
643             if match:
644                 style = match.group(4).lower()
645                 if re_Separator.match(style):
646                     if len(separator) > 0:
647                         lines[i:i+1] = separator
648                     else:
649                         # FIXME: If this style was redefined in an include file,
650                         # we should replace the real style and not this default.
651                         lines[i:i+1] = [b'      Category              MainText',
652                                         b'      KeepEmpty             1',
653                                         b'      Margin                Dynamic',
654                                         b'      LatexType             Paragraph',
655                                         b'      LatexName             dummy',
656                                         b'      ParIndent             MM',
657                                         b'      Align                 Block',
658                                         b'      LabelType             Static',
659                                         b'      LabelString           "--- Separate Environment ---"',
660                                         b'      LabelFont',
661                                         b'        Family              Roman',
662                                         b'        Series              Medium',
663                                         b'        Size                Normal',
664                                         b'        Color               Blue',
665                                         b'      EndFont',
666                                         b'      HTMLLabel             NONE']
667             i += 1
668             continue
669
670         if format == 48:
671             # The default of ResetsFont in LyX changed from true to false,
672             # because it is now used for all InsetLayouts, not only flex ones.
673             # Therefore we need to set it to true for all flex insets which do
674             # do not already have a ResetsFont.
675             match = re_InsetLayout2.match(lines[i])
676             if not match:
677                 i += 1
678                 continue
679
680             name = match.group(1).lower()
681             if name != b"flex" and name != b"\"flex\"" and name[0:5] != b"flex:" and name [0:6] != b"\"flex:":
682                 i += 1
683                 continue
684
685             resetsfont_found = False
686             inherited = False
687             notdone = True
688             while i < len(lines):
689               match = re_ResetsFont.match(lines[i])
690               if match:
691                   resetsfont_found = True
692               else:
693                 match = re_CopyStyle.match(lines[i])
694                 if match:
695                   inherited = True
696                 else:
697                   match = re_End.match(lines[i])
698                   if match:
699                     break
700               i += 1
701             if not resetsfont_found and not inherited:
702               lines.insert(i, b"\tResetsFont true")
703
704             continue
705
706         if 44 <= format <= 47:
707             # nothing to do.
708             i += 1
709             continue
710
711         if format == 43:
712           match = re_LabelTypeIsCounter.match(lines[i])
713           if match:
714             if inchapter:
715              lines[i] = match.group(1) + b"LabelType" + match.group(2) + b"Above"
716             else:
717               lines[i] = match.group(1) + b"LabelType" + match.group(2) + b"Static"
718
719           match = re_TopEnvironment.match(lines[i])
720           if match:
721             lines[i] = match.group(1) + b"LabelType" + match.group(2) + b"Above"
722
723           match = re_CenteredEnvironment.match(lines[i])
724           if match:
725             lines[i] = match.group(1) + b"LabelType" + match.group(2) + b"Centered"
726
727           if inchapter:
728             match = re_Style.match(lines[i])
729             if match:
730               inchapter = False
731           else:
732             match = re_ChapterStyle.match(lines[i])
733             if match:
734               inchapter = True
735
736           i += 1
737           continue
738
739         if format == 42:
740           if lines[i] == b"InsetLayout Caption":
741             lines[i] = b"InsetLayout Caption:Standard"
742           i += 1
743           continue
744
745         if format == 41:
746             # nothing to do.
747             i += 1
748             continue
749
750         if format == 40:
751             # reset counters on Style beginning
752             match = re_Style.match(lines[i])
753             if match:
754                 opts = 0
755                 reqs = 0
756                 i += 1
757                 continue
758             match = re_OptArgs.match(lines[i])
759             if match:
760                 # Save number of optional arguments
761                 space1 = match.group(1)
762                 opts = int(match.group(3))
763                 # OptionalArgs 0 > ResetArgs 1
764                 if opts == 0:
765                     lines[i] = space1 + b"ResetArgs\t1"
766                     i += 1
767                 else:
768                     del lines[i]
769                 continue
770             match = re_ReqArgs.match(lines[i])
771             if match:
772                 # Save number of required arguments
773                 space1 = match.group(1)
774                 reqs = int(match.group(3))
775                 del lines[i]
776                 continue
777             # Insert the required number of arguments at the end of the style definition
778             match = re_End.match(lines[i])
779             if match:
780                 newarg = ['']
781                 # First the optionals (this is the required order pre 2.1)
782                 if opts > 0:
783                     if opts == 1:
784                         newarg = [ b'%sArgument 1' % (space1),
785                                    b'%s\tLabelString\t\"Optional Layout Argument\"' % (space1),
786                                    b'%sEndArgument' % (space1)]
787                     elif opts > 1:
788                         actopt = 1
789                         while actopt < (opts + 1):
790                             newarg += [ b'%sArgument %d' % (space1, actopt),
791                                b'%s\tLabelString\t\"Optional Layout Argument %d\"' % (space1, actopt),
792                                b'%sEndArgument' % (space1)]
793                             actopt += 1
794                 # Now the mandatories
795                 if reqs > 0:
796                     actopt = opts + 1
797                     while actopt < (opts +  reqs + 1):
798                         newarg += [ b'%sArgument %d' % (space1, actopt),
799                            b'%s\tLabelString\t"Required Layout Argument %d"' % (space1, actopt - opts),
800                            b'%s\tMandatory\t1' % (space1),
801                            b'%sEndArgument' % (space1)]
802                         actopt += 1
803                 # Since we replace the "End" line, re-add this line
804                 if len(newarg) > 1:
805                     newarg += [b'End']
806                     lines[i:i+1] = newarg
807                     i += len(newarg)
808                 # Reset the counters
809                 opts = 0
810                 reqs = 0
811             i += 1
812             continue
813
814         if format == 39:
815             # There is a conversion with format 40, but it is done within the
816             # initial comment block and so is above.
817             i += 1
818             continue
819
820         if format == 37 or format == 38:
821             i += 1
822             continue
823
824         if format == 36:
825             match = re_CiteFormat.match(lines[i])
826             if match and match.group(4) == b"":
827                 lines[i] = match.group(0) + b" default"
828             i += 1
829             continue
830
831         if format == 35:
832           i += 1
833           continue
834
835         if format == 34:
836           match = re_QInsetLayout2.match(lines[i])
837           if not match:
838             match = re_InsetLayout2.match(lines[i])
839           if not match:
840             match = re_CopyStyle2.match(lines[i])
841             if not match:
842               i += 1
843               continue
844             style = match.group(2)
845
846             if flexstyles.count(style):
847               lines[i] = match.group(1) + b"\"Flex:" + style + b"\""
848             i += 1
849             continue
850
851           name = match.group(1)
852           names = name.split(b":", 1)
853           if len(names) > 1 and names[0] == b"Flex":
854             i += 1
855             continue
856
857           isflex = False
858           for j in range(i + 1, len(lines)):
859             if re_IsFlex.match(lines[j]):
860               isflex = True
861               break
862             if re_End.match(lines[j]):
863               break
864
865           if isflex:
866             flexstyles.append(name)
867             lines[i] = b"InsetLayout \"Flex:" + name + b"\""
868
869           i += 1
870           continue
871
872         if format == 33:
873           m = re_Fill.match(lines[i])
874           if m:
875             lines[i] = b""
876           i += 1
877           continue
878
879         if format == 32:
880           match = re_NeedsFloatPkg.match(lines[i])
881           if match:
882             space = match.group(1)
883             val = match.group(2)
884             lines[i] = space + b"UsesFloatPkg " + val
885             newval = b'true'
886             if val == b'1' or val.lower() == b'true':
887               newval = b'false'
888             lines.insert(i, space + b"IsPredefined " + newval)
889             i += 1
890           i += 1
891           continue
892
893         # Only new features
894         if 29 <= format <= 31:
895           i += 1
896           continue
897
898         if format == 28:
899           match = re_InsetLayout.match(lines[i])
900           if match:
901             lines[i] = b"InsetLayout Flex:" + match.group(1)
902           else:
903             match = re_QInsetLayout.match(lines[i])
904             if match:
905               lines[i] = b"InsetLayout \"Flex:" + match.group(1) + b"\""
906             else:
907               match = re_InsetLayout_CopyStyle.match(lines[i])
908               if match:
909                 lines[i] = b"\tCopyStyle Flex:" + match.group(1)
910               else:
911                 match = re_QInsetLayout_CopyStyle.match(lines[i])
912                 if match:
913                   lines[i] = b"\tCopyStyle \"Flex:" + match.group(1) + b"\""
914           i += 1
915           continue
916
917         # Only new features
918         if 24 <= format <= 27:
919           i += 1
920           continue
921
922         if format == 23:
923           match = re_Float.match(lines[i])
924           i += 1
925           if not match:
926             continue
927           # we need to do two things:
928           # (i)  Convert Builtin to NeedsFloatPkg
929           # (ii) Write ListCommand lines for the builtin floats table and figure
930           builtin = False
931           cmd = b""
932           while True and i < len(lines):
933             m1 = re_End.match(lines[i])
934             if m1:
935               if builtin and cmd:
936                 line = b"    ListCommand " + cmd
937                 lines.insert(i, line)
938                 i += 1
939               break
940             m2 = re_Builtin.match(lines[i])
941             if m2:
942               builtin = True
943               ws1 = m2.group(1)
944               arg = m2.group(2)
945               newarg = b""
946               if re_True.match(arg):
947                 newarg = b"false"
948               else:
949                 newarg = b"true"
950               lines[i] = ws1 + b"NeedsFloatPkg " + newarg
951             m3 = re_Type.match(lines[i])
952             if m3:
953               fltype = m3.group(1)
954               fltype = fltype.lower()
955               if fltype == b"table":
956                 cmd = b"listoftables"
957               elif fltype == b"figure":
958                 cmd = b"listoffigures"
959               # else unknown, which is why we're doing this
960             i += 1
961           continue
962
963         # This just involved new features, not any changes to old ones
964         if 14 <= format <= 22:
965           i += 1
966           continue
967
968         # Rename I18NPreamble to BabelPreamble
969         if format == 13:
970             match = re_I18nPreamble.match(lines[i])
971             if match:
972                 lines[i] = match.group(1) + b"BabelPreamble"
973                 i += 1
974                 match = re_EndI18nPreamble.match(lines[i])
975                 while i < len(lines) and not match:
976                     i += 1
977                     match = re_EndI18nPreamble.match(lines[i])
978                 lines[i] = match.group(1) + b"EndBabelPreamble"
979                 i += 1
980                 continue
981
982         # These just involved new features, not any changes to old ones
983         if format == 11 or format == 12:
984           i += 1
985           continue
986
987         if format == 10:
988             match = re_UseMod.match(lines[i])
989             if match:
990                 module = match.group(1)
991                 lines[i] = b"DefaultModule " + module
992             i += 1
993             continue
994
995         if format == 9:
996             match = re_Counter.match(lines[i])
997             if match:
998                 counterline = i
999                 i += 1
1000                 while i < len(lines):
1001                     namem = re_Name.match(lines[i])
1002                     if namem:
1003                         name = namem.group(1)
1004                         lines.pop(i)
1005                         lines[counterline] = b"Counter %s" % name
1006                         # we don't need to increment i
1007                         continue
1008                     endem = re_End.match(lines[i])
1009                     if endem:
1010                         i += 1
1011                         break
1012                     i += 1
1013             i += 1
1014             continue
1015
1016         if format == 8:
1017             # We want to scan for ams-type includes and, if we find them,
1018             # add corresponding UseModule tags to the layout.
1019             match = re_AMSMaths.match(lines[i])
1020             if match:
1021                 addstring(b"theorems-ams", usemodules)
1022                 addstring(b"theorems-ams-extended", usemodules)
1023                 addstring(b"theorems-sec", usemodules)
1024                 lines.pop(i)
1025                 continue
1026             match = re_AMSMathsPlain.match(lines[i])
1027             if match:
1028                 addstring(b"theorems-starred", usemodules)
1029                 lines.pop(i)
1030                 continue
1031             match = re_AMSMathsSeq.match(lines[i])
1032             if match:
1033                 addstring(b"theorems-ams", usemodules)
1034                 addstring(b"theorems-ams-extended", usemodules)
1035                 lines.pop(i)
1036                 continue
1037             i += 1
1038             continue
1039
1040         # These just involved new features, not any changes to old ones
1041         if 5 <= format <= 7:
1042           i += 1
1043           continue
1044
1045         if format == 4:
1046             # Handle conversion to long CharStyle names
1047             match = re_CharStyle.match(lines[i])
1048             if match:
1049                 lines[i] = b"InsetLayout CharStyle:%s" % (match.group(3))
1050                 i += 1
1051                 lines.insert(i, b"\tLyXType charstyle")
1052                 i += 1
1053                 lines.insert(i, b"")
1054                 lines[i] = b"\tLabelString %s" % (match.group(3))
1055             i += 1
1056             continue
1057
1058         if format == 3:
1059             # convert 'providesamsmath x',  'providesmakeidx x',  'providesnatbib x',  'providesurl x' to
1060             #         'provides amsmath x', 'provides makeidx x', 'provides natbib x', 'provides url x'
1061             # x is either 0 or 1
1062             match = re_Provides.match(lines[i])
1063             if match:
1064                 lines[i] = b"%sProvides %s%s%s" % (match.group(1), match.group(2).lower(),
1065                                                   match.group(3), match.group(4))
1066             i += 1
1067             continue
1068
1069         if format == 2:
1070             caption = []
1071
1072             # delete caption styles
1073             match = re_Style.match(lines[i])
1074             if match:
1075                 style = match.group(4).lower()
1076                 if style == b"caption":
1077                     del lines[i]
1078                     while i < len(lines) and not re_End.match(lines[i]):
1079                         caption.append(lines[i])
1080                         del lines[i]
1081                     if i == len(lines):
1082                         error('Incomplete caption style.')
1083                     else:
1084                         del lines[i]
1085                         continue
1086
1087             # delete undefinition of caption styles
1088             match = re_NoStyle.match(lines[i])
1089             if match:
1090                 style = match.group(4).lower()
1091                 if style == b"caption":
1092                     del lines[i]
1093                     continue
1094
1095             # replace the CopyStyle statement with the definition of the real
1096             # style. This may result in duplicate statements, but that is OK
1097             # since the second one will overwrite the first one.
1098             match = re_CopyStyle.match(lines[i])
1099             if match:
1100                 style = match.group(4).lower()
1101                 if style == b"caption":
1102                     if len(caption) > 0:
1103                         lines[i:i+1] = caption
1104                     else:
1105                         # FIXME: This style comes from an include file, we
1106                         # should replace the real style and not this default.
1107                         lines[i:i+1] = [b'      Margin                First_Dynamic',
1108                                         b'      LatexType             Command',
1109                                         b'      LatexName             caption',
1110                                         b'      NeedProtect           1',
1111                                         b'      LabelSep              xx',
1112                                         b'      ParSkip               0.4',
1113                                         b'      TopSep                0.5',
1114                                         b'      Align                 Center',
1115                                         b'      AlignPossible         Center',
1116                                         b'      LabelType             Sensitive',
1117                                         b'      LabelString           "Senseless!"',
1118                                         b'      OptionalArgs          1',
1119                                         b'      LabelFont',
1120                                         b'        Series              Bold',
1121                                         b'      EndFont']
1122
1123             i += 1
1124             continue
1125
1126         # Delete MaxCounter and remember the value of it
1127         match = re_MaxCounter.match(lines[i])
1128         if match:
1129             level = match.group(4).lower()
1130             if level == b"counter_chapter":
1131                 maxcounter = 0
1132             elif level == b"counter_section":
1133                 maxcounter = 1
1134             elif level == b"counter_subsection":
1135                 maxcounter = 2
1136             elif level == b"counter_subsubsection":
1137                 maxcounter = 3
1138             elif level == b"counter_paragraph":
1139                 maxcounter = 4
1140             elif level == b"counter_subparagraph":
1141                 maxcounter = 5
1142             elif level == b"counter_enumi":
1143                 maxcounter = 6
1144             elif level == b"counter_enumii":
1145                 maxcounter = 7
1146             elif level == b"counter_enumiii":
1147                 maxcounter = 8
1148             del lines[i]
1149             continue
1150
1151         # Replace line
1152         #
1153         # LabelType Counter_EnumI
1154         #
1155         # with two lines
1156         #
1157         # LabelType Counter
1158         # LabelCounter EnumI
1159         #
1160         match = re_LabelType.match(lines[i])
1161         if match:
1162             label = match.group(4)
1163             # Remember indenting space for later reuse in added lines
1164             space1 = match.group(1)
1165             # Remember the line for adding the LabelCounter later.
1166             # We can't do it here because it could shift latextype_line etc.
1167             labeltype_line = i
1168             if label[:8].lower() == b"counter_":
1169                 counter = label[8:].lower()
1170                 lines[i] = re_LabelType.sub(b'\\1\\2\\3Counter', lines[i])
1171
1172         # Remember the LabelString line
1173         match = re_LabelString.match(lines[i])
1174         if match:
1175             labelstring = match.group(4)
1176             labelstring_line = i
1177
1178         # Remember the LabelStringAppendix line
1179         match = re_LabelStringAppendix.match(lines[i])
1180         if match:
1181             labelstringappendix = match.group(4)
1182             labelstringappendix_line = i
1183
1184         # Remember the LatexType line
1185         match = re_LatexType.match(lines[i])
1186         if match:
1187             latextype = match.group(4).lower()
1188             latextype_line = i
1189
1190         # Remember the TocLevel line
1191         match = re_TocLevel.match(lines[i])
1192         if match:
1193             toclevel = match.group(4).lower()
1194
1195         # Reset variables at the beginning of a style definition
1196         match = re_Style.match(lines[i])
1197         if match:
1198             style = match.group(4).lower()
1199             counter = b""
1200             toclevel = b""
1201             label = b""
1202             space1 = b""
1203             labelstring = b""
1204             labelstringappendix = b""
1205             labelstring_line = -1
1206             labelstringappendix_line = -1
1207             labeltype_line = -1
1208             latextype = b""
1209             latextype_line = -1
1210
1211         if re_End.match(lines[i]):
1212
1213             # Add a line "LatexType Bib_Environment" if LabelType is Bibliography
1214             # (or change the existing LatexType)
1215             if label.lower() == b"bibliography":
1216                 if (latextype_line < 0):
1217                     lines.insert(i, b"%sLatexType Bib_Environment" % space1)
1218                     i += 1
1219                 else:
1220                     lines[latextype_line] = re_LatexType.sub(b'\\1\\2\\3Bib_Environment', lines[latextype_line])
1221
1222             # Change "LabelType Static" to "LabelType Itemize" for itemize environments
1223             if latextype == b"item_environment" and label.lower() == b"static":
1224                 lines[labeltype_line] = re_LabelType.sub(b'\\1\\2\\3Itemize', lines[labeltype_line])
1225
1226             # Change "LabelType Counter_EnumI" to "LabelType Enumerate" for enumerate environments
1227             if latextype == b"item_environment" and label.lower() == b"counter_enumi":
1228                 lines[labeltype_line] = re_LabelType.sub(b'\\1\\2\\3Enumerate', lines[labeltype_line])
1229                 # Don't add the LabelCounter line later
1230                 counter = ""
1231
1232             # Replace
1233             #
1234             # LabelString "Chapter"
1235             #
1236             # with
1237             #
1238             # LabelString "Chapter \arabic{chapter}"
1239             #
1240             # if this style has a counter. Ditto for LabelStringAppendix.
1241             # This emulates the hardcoded article style numbering of 1.3
1242             #
1243             if counter != b"":
1244                 if style in counters:
1245                     if labelstring_line < 0:
1246                         lines.insert(i, b'%sLabelString "%s"' % (space1, counters[style]))
1247                         i += 1
1248                     else:
1249                         new_labelstring = concatenate_label(labelstring, counters[style])
1250                         lines[labelstring_line] = re_LabelString.sub(
1251                                 b'\\1\\2\\3%s' % new_labelstring.replace(b"\\", b"\\\\"),
1252                                 lines[labelstring_line])
1253                 if style in appendixcounters:
1254                     if labelstringappendix_line < 0:
1255                         lines.insert(i, b'%sLabelStringAppendix "%s"' % (space1, appendixcounters[style]))
1256                         i += 1
1257                     else:
1258                         new_labelstring = concatenate_label(labelstring, appendixcounters[style])
1259                         lines[labelstringappendix_line] = re_LabelStringAppendix.sub(
1260                                 b'\\1\\2\\3%s' % new_labelstring.replace(b"\\", b"\\\\"),
1261                                 lines[labelstringappendix_line])
1262
1263                 # Now we can safely add the LabelCounter line
1264                 lines.insert(labeltype_line + 1, b"%sLabelCounter %s" % (space1, counter))
1265                 i += 1
1266
1267             # Add the TocLevel setting for sectioning styles
1268             if toclevel == b"" and style in toclevels and maxcounter <= toclevels[style]:
1269                 lines.insert(i, b'%s\tTocLevel %d' % (space1, toclevels[style]))
1270                 i += 1
1271
1272         i += 1
1273
1274     if only_comment:
1275         lines.insert(i, b"Format 2")
1276     if usemodules:
1277         i = formatline + 1
1278         for mod in usemodules:
1279             lines.insert(i, b"UseModule " + mod)
1280             i += 1
1281
1282     return format + 1
1283
1284
1285 def main(argv):
1286     args = {}
1287     args["description"] = "Convert layout file <inputfile> to a newer format."
1288
1289     parser = argparse.ArgumentParser(**args)
1290
1291     parser.add_argument("-t", "--to", type=int, dest="format", default= currentFormat,
1292                         help=("destination layout format, default %i (latest)") % currentFormat)
1293     parser.add_argument("input_file", nargs='?', type=cmd_arg, default=None,
1294                         help="input file (default stdin)")
1295     parser.add_argument("output_file", nargs='?', type=cmd_arg, default=None,
1296                         help="output file (default stdout)")
1297
1298     options = parser.parse_args(argv[1:])
1299
1300     # Open files
1301     if options.input_file:
1302         source = open(options.input_file, 'rb')
1303     elif PY2:
1304         source = sys.stdin
1305     else:
1306         source = sys.stdin.buffer
1307
1308     if options.output_file:
1309         output = open(options.output_file, 'wb')
1310     elif PY2:
1311         output = sys.stdout
1312     else:
1313         output = sys.stdout.buffer
1314
1315     if options.format > currentFormat:
1316         error("Format %i does not exist" % options.format);
1317
1318     # Do the real work
1319     lines = read(source)
1320     format = 1
1321     while (format < options.format):
1322         format = convert(lines, options.format)
1323     write(output, lines)
1324
1325     # Close files
1326     if options.input_file:
1327         source.close()
1328     if options.output_file:
1329         output.close()
1330
1331     return 0
1332
1333
1334 if __name__ == "__main__":
1335     main(sys.argv)