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