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