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