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