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