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