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