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