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