]> git.lyx.org Git - lyx.git/blob - lib/scripts/layout2layout.py
Fix bug #12181
[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 = 93
15
16
17 # Incremented to format 4, 6 April 2007, lasgouttes
18 # Introduction of generic "Provides" declaration
19
20 # Incremented to format 5, 22 August 2007 by vermeer
21 # InsetLayout material
22
23 # Incremented to format 6, 7 January 2008 by spitz
24 # Requires tag added to layout files
25
26 # Incremented to format 7, 24 March 2008 by rgh
27 # AddToPreamble tag added to layout files
28
29 # Incremented to format 8, 25 July 2008 by rgh
30 # UseModule tag added to layout files
31 # CopyStyle added to InsetLayout
32
33 # Incremented to format 9, 5 October 2008 by rgh
34 # ForcePlain and CustomPars tags added to InsetLayout
35
36 # Incremented to format 10, 6 October 2008 by rgh
37 # Change format of counters
38
39 # Incremented to format 11, 14 October 2008 by rgh
40 # Add ProvidesModule, ExcludesModule tags
41
42 # Incremented to format 12, 10 January 2009 by gb
43 # Add I18NPreamble tag
44
45 # Incremented to format 13, 5 February 2009 by rgh
46 # Add InToc tag for InsetLayout
47
48 # Incremented to format 14, 14 February 2009 by gb
49 # Rename I18NPreamble to BabelPreamble and add LangPreamble
50
51 # Incremented to format 15, 28 May 2009 by lasgouttes
52 # Add new tag OutputFormat; modules can be conditioned on feature
53 # "from->to".
54
55 # Incremented to format 16, 5 June 2009 by rgh
56 # Add new tags for Text Class:
57 #   HTMLPreamble, HTMLAddToPreamble
58 # For Layout:
59 #   HTMLTag, HTMLAttr, HTMLLabel, HTMLLabelAttr, HTMLItem, HTMLItemAttr
60 #   HTMLStyle, and HTMLPreamble
61 # For InsetLayout:
62 #   HTMLTag, HTMLAttr, HTMLStyle, and HTMLPreamble
63 # For Floats:
64 #   HTMLType, HTMLClass, HTMLStyle
65
66 # Incremented to format 17, 12 August 2009 by rgh
67 # Add IfStyle and IfCounter tags for layout.
68
69 # Incremented to format 18, 27 October 2009 by rgh
70 # Added some new tags for HTML output.
71
72 # Incremented to format 19, 17 November 2009 by rgh
73 # Added InPreamble tag.
74
75 # Incremented to format 20, 17 December 2009 by rgh
76 # Added ContentAsLabel tag.
77
78 # Incremented to format 21, 12 January 2010 by rgh
79 # Added HTMLTocLayout and HTMLTitle tags.
80
81 # Incremented to format 22, 20 January 2010 by rgh
82 # Added HTMLFormat tag to Counters.
83
84 # Incremented to format 23, 13 February 2010 by spitz
85 # Added Spellcheck tag.
86
87 # Incremented to format 24, 5 March 2010 by rgh
88 # Changed LaTeXBuiltin tag to NeedsFloatPkg and
89 # added new tag ListCommand.
90
91 # Incremented to format 25, 12 March 2010 by rgh
92 # Added RefPrefix tag for layouts and floats.
93
94 # Incremented to format 26, 29 March 2010 by rgh
95 # Added CiteFormat.
96
97 # Incremented to format 27, 4 June 2010 by rgh
98 # Added RequiredArgs tag.
99
100 # Incremented to format 28, 6 August 2010 by lasgouttes
101 # Added ParbreakIsNewline tag for Layout and InsetLayout.
102
103 # Incremented to format 29, 10 August 2010 by rgh
104 # Changed Custom:Style, CharStyle:Style, and Element:Style
105 # uniformly to Flex:Style.
106
107 # Incremented to format 30, 13 August 2010 by rgh
108 # Introduced ResetsFont tag for InsetLayout.
109
110 # Incremented to format 31, 12 January 2011 by rgh
111 # Introducted NoCounter tag.
112
113 # Incremented to format 32, 30 January 2011 by forenr
114 # Added Display tag for InsetLayout
115
116 # Incremented to format 33, 2 February 2011 by rgh
117 # Changed NeedsFloatPkg to UsesFloatPkg
118
119 # Incremented to format 34, 28 March 2011 by rgh
120 # Remove obsolete Fill_(Top|Bottom) tags
121
122 # Incremented to format 35, 28 March 2011 by rgh
123 # Try to add "Flex:" to any flex insets that don't have it.
124
125 # Incremented to format 36, 7 December 2011, by rgh
126 # Added HTMLStyles and AddToHTMLStyles tags.
127
128 # Incremented to format 37, 29 February 2012 by jrioux
129 # Implement the citation engine machinery in layouts.
130 # Change CiteFormat to CiteFormat (default|authoryear|numerical).
131
132 # Incremented to format 38, 08 April 2012 by gb
133 # Introduce LangPreamble and BabelPreamble for InsetLayout.
134
135 # Incremented to format 39, 15 April 2012 by sanda
136 # Introduce styling of branches via "InsetLayout Branch:".
137
138 # Incremented to format 40, 10 October 2012 by rgh
139 # Re-do layout names for layout categories
140
141 # Incremented to format 41, 20 November 2012 by spitz
142 # New Argument syntax
143
144 # Incremented to format 42, 22 December 2012 by spitz
145 # New Style tag "ItemCommand"
146
147 # Incremented to format 43, 30 December 2012 by spitz
148 # Extended InsetCaption format
149
150 # Incremented to format 44, 9 February 2013 by rgh
151 # Remove COUNTER label style; rename as STATIC
152 # Rename TOP_ENVIRONMENT to ABOVE and CENTERED_TOP_ENVIRONMENT to CENTERED
153
154 # Incremented to format 45, 12 February 2013 by rgh
155 # New Tag "NoInsetLayout"
156
157 # Incremented to format 46, 15 May 2013 by gb
158 # New Tag "ForceLocal"
159
160 # Incremented to format 47, 23 May 2013 by rgh
161 # Add PackageOptions tag
162
163 # Incremented to format 48, 31 May 2013 by rgh
164 # Add InitialValue tag for counters
165
166 # Incremented to format 49, 10 Feb 2014 by gb
167 # Change default of "ResetsFont" tag to false
168
169 # Incremented to format 50, 9 May 2014 by forenr
170 # Removal of "Separator" layouts
171
172 # Incremented to format 51, 29 May 2014 by spitz
173 # New Style tag "ToggleIndent"
174
175 # Incremented to format 52, 1 December 2014 by spitz
176 # New InsetLayout tag "ForceOwnlines"
177
178 # Incremented to format 53, 7 December 2014 by spitz
179 # New InsetLayout tag "ObsoletedBy"
180
181 # Incremented to format 54, 11 Jan 2014 by gb
182 # New InsetLayout tag "FixedWidthPreambleEncoding"
183
184 # Incremented to format 55, 20 April 2015 by spitz
185 # New InsetLayout and Layout tags "PassThruChars"
186
187 # Incremented to format 56, 20 May 2015 by spitz
188 # New Float tags "AllowedPlacement", "AllowsWide", "AllowsSideways"
189
190 # Incremented to format 57, 30 May 2015 by spitz
191 # New Layout tag "ParagraphGroup"
192
193 # Incremented to format 58, 5 December 2015, by rgh
194 # New Layout tag "ProvideStyle"
195 # Change "IfStyle" to "ModifyStyle"
196
197 # Incremented to format 59, 22 November 2015 by gm
198 # New Tag "OutlinerName"
199 # New Layout tags "AddToToc", "IsTocCaption"
200 # New Layout argument tag "IsTocCaption"
201
202 # Incremented to format 60, 25 March 2016 by lasgouttes
203 # Rename caption subtype LongTableNoNumber to Unnumbered
204
205 # Incremented to format 61, 14 October 2016 by spitz
206 # New Layout tags "ResumeCounter", "StepMasterCounter"
207
208 # Incremented to format 62, 21 October 2016 by spitz
209 # New Layout argument tag "PassThru"
210
211 # Incremented to format 63, 7 January 2017 by spitz
212 # - New textclass tags CiteFramework, MaxCiteNames (for cite engines)
213 # - Extended InsetCite syntax.
214
215 # Incremented to format 64, 30 August 2017 by rgh
216 # Strip leading and trailing spaces from LabelString,
217 # LabelStringAppendix, and EndLabelString, and LabelCounter,
218 # to conform to what we used to do.
219
220 # Incremented to format 65, 16 October 2017 by spitz
221 # Color collapsable -> collapsible
222
223 # Incremented to format 66, 28 December 2017 by spitz
224 # New Layout tags "AutoNests ... EndAutoNests" and
225 # "IsAutoNestedBy ... EndIsAutoNestedBy"
226
227 # Incremented to format 67, 14 April 2018 by spitz
228 # New Layout tag "NeedsCProtect"
229
230 # Incremented to format 68, 21 May 2018 by spitz
231 # New Layout tag "AddToCiteEngine"
232
233 # Incremented to format 69, 16 August 2018 by spitz
234 # New argument type "listpreamble"
235
236 # Incremented to format 70, 5 June 2018 by rkh
237 # New InsetLayout tag EditExternal
238
239 # Incremented to format 71, 12 March 2019 by spitz
240 # New [Inset]Layout tag NeedMBoxProtect
241
242 # Incremented to format 72, 26 March 2019 by spitz
243 # New TextClass tag TableStyle
244
245 # Incremented to format 73, 18 April 2019 by spitz
246 # New InsetLayout tag MenuString
247
248 # Incremented to format 74, 18 April 2019 by spitz
249 # New InsetLayout and Argument tag NewlineCmd
250
251 # Incremented to format 75, 2 June 2019 by spitz
252 # New Argument tags FreeSpacing, InsertOnNewline
253 # New InsetLayout tag ParbreakIgnored
254
255 # Incremented to format 76, 8 July 2019 by spitz
256 # New textclass tag BibInToc
257
258 # Incremented to format 77, 6 August 2019 by spitz
259 # New textclass tag PageSize (= default page size) 
260 # and textclass option PageSize (= list of available page sizes)
261
262 # Incremented to format 78, 6 August 2019 by spitz
263 # New textclass tag FontsizeFormat
264
265 # Incremented to format 79, 7 August 2019 by spitz
266 # New textclass tag PagesizeFormat
267
268 # Incremented to format 80, 12 August 2019 by spitz
269 # New float option Requires
270
271 # Incremented to format 81, 12 August 2019 by rikiheck
272 # New tag GuiName for counters
273
274 # Incremented to format 82, 4 June 2017 by tcuvelier
275 # - Add new tags for Layout:
276 #   DocBookTag, DocBookAttr, DocBookInInfo,
277 #   DocBookWrapperTag, DocBookWrapperAttr,
278 #   DocBookItemWrapperTag, DocBookItemWrapperAttr,
279 #   DocBookItemTag, DocBookItemAttr,
280 #   DocBookLabelTag, DocBookLabelAttr
281 # - Removed tag Header from ClassOptionsClassOptions
282 # - Removed tag Element for flex insets
283
284 # Incremented to format 83, 2 August 2020 by tcuvelier
285 # New tags DocBookWrapperMergeWithPrevious and DocBookAbstract
286
287 # Incremented to format 84, 17 August 2020 by tcuvelier
288 # New tags DocBookTagType, DocBookWrapperTagTagType,
289 # DocBookItemWrapperTagTagType, DocBookItemTagTagType,
290 # DocBookLabelTag
291
292 # Incremented to format 85, 7 October 2020 by tcuvelier
293 # New tags DocBookInnerTag, DocBookInnerAttr,
294 # DocBookInnerTagType
295
296 # Incremented to format 86, 20 October 2020 by tcuvelier
297 # New tag DocBookSection.
298
299 # Incremented to format 87, 2 November 2020 by rkh
300
301 # Incremented to format 88, 28 November 2020 by tcuvelier
302 # New tag DocBookNotInPara.
303
304 # Incremented to format 89, 5 December 2020 by rkh
305 # New tag LaTeXName for counters
306
307 # Incremented to format 90, 11 December 2020 by spitz
308 # Use semantic label colors
309
310 # Incremented to format 91, 25 January 2021 by spitz
311 # InputGlobal tag
312
313 # Incremented to format 92, 30 January 2021 by spitz
314 # Add ProvideInsetLayout and ModifyInsetLayout
315
316 # Incremented to format 93, 13 February 2021 by spitz
317 # Add DocBookNoFontInside
318
319 # Do not forget to document format change in Customization
320 # Manual (section "Declaring a new text class").
321
322 # You might also want to consider running the
323 # development/tools/updatelayouts.py script to update all
324 # layout files to the new format.
325
326
327 import os, re, sys
328 import argparse
329
330 # Provide support for both python 2 and 3
331 # (copied from lyx2lyx)
332 PY2 = sys.version_info[0] == 2
333 if PY2:
334     # argparse returns strings in the commandline encoding, we need to convert.
335     # sys.getdefaultencoding() would not always be correct, see
336     # http://legacy.python.org/dev/peps/pep-0383/
337     def cmd_arg(arg):
338         return arg.decode(sys.getfilesystemencoding())
339 else:
340     cmd_arg = str
341 # End of code to support for both python 2 and 3
342
343
344 def error(message):
345     sys.stderr.write(message + '\n')
346     sys.exit(1)
347
348
349 def trim_bom(line):
350     " Remove byte order mark."
351     if line[0:3] == "\357\273\277":
352         return line[3:]
353     else:
354         return line
355
356
357 def read(source):
358     " Read input file and strip lineendings."
359     lines = source.read().splitlines() or ['']
360     lines[0] = trim_bom(lines[0])
361     return lines
362
363
364 def write(output, lines):
365     " Write output file with native lineendings."
366     output.write(os.linesep.encode('ascii').join(lines)
367                  + os.linesep.encode('ascii'))
368
369
370 # Concatenates old and new in an intelligent way:
371 # If old is wrapped in ", they are stripped. The result is wrapped in ".
372 def concatenate_label(old, new):
373     # Don't use strip as long as we support python 1.5.2
374     if old[0] == b'"':
375         return old[0:-1] + new + b'"'
376     else:
377         return b'"' + old + new + b'"'
378
379
380 # appends a string to a list unless it's already there
381 def addstring(s, l):
382     if l.count(s) > 0:
383         return
384     l.append(s)
385
386
387 def convert(lines, end_format):
388     " Convert to new format."
389     re_Comment = re.compile(b'^(\\s*)#')
390     re_Counter = re.compile(b'\\s*Counter\\s*', re.IGNORECASE)
391     re_Name = re.compile(b'\\s*Name\\s+(\\S+)\\s*', re.IGNORECASE)
392     re_UseMod = re.compile(b'^\\s*UseModule\\s+(.*)', re.IGNORECASE)
393     re_Empty = re.compile(b'^(\\s*)$')
394     re_Format = re.compile(b'^(\\s*)(Format)(\\s+)(\\S+)', re.IGNORECASE)
395     re_Preamble = re.compile(b'^(\\s*)Preamble', re.IGNORECASE)
396     re_EndPreamble = re.compile(b'^(\\s*)EndPreamble', re.IGNORECASE)
397     re_LangPreamble = re.compile(b'^(\\s*)LangPreamble', re.IGNORECASE)
398     re_EndLangPreamble = re.compile(b'^(\\s*)EndLangPreamble', re.IGNORECASE)
399     re_BabelPreamble = re.compile(b'^(\\s*)BabelPreamble', re.IGNORECASE)
400     re_EndBabelPreamble = re.compile(b'^(\\s*)EndBabelPreamble', re.IGNORECASE)
401     re_MaxCounter = re.compile(b'^(\\s*)(MaxCounter)(\\s+)(\\S+)', re.IGNORECASE)
402     re_LabelType = re.compile(b'^(\\s*)(LabelType)(\\s+)(\\S+)', re.IGNORECASE)
403     re_LabelString = re.compile(b'^(\\s*)(LabelString)(\\s+)(("[^"]+")|(\\S+))', re.IGNORECASE)
404     re_LabelStringAppendix = re.compile(b'^(\\s*)(LabelStringAppendix)(\\s+)(("[^"]+")|(\\S+))', re.IGNORECASE)
405     re_LatexType = re.compile(b'^(\\s*)(LatexType)(\\s+)(\\S+)', re.IGNORECASE)
406     re_Style = re.compile(b'^(\\s*)(Style)(\\s+)(\\S+)', re.IGNORECASE)
407     re_IfStyle = re.compile(b'^(\\s*)IfStyle(\\s+\\S+)', re.IGNORECASE)
408     re_CopyStyle = re.compile(b'^(\\s*)(CopyStyle)(\\s+)(\\S+)', re.IGNORECASE)
409     re_NoStyle = re.compile(b'^(\\s*)(NoStyle)(\\s+)(\\S+)', re.IGNORECASE)
410     re_End = re.compile(b'^(\\s*)(End)(\\s*)$', re.IGNORECASE)
411     re_Provides = re.compile(b'^(\\s*)Provides(\\S+)(\\s+)(\\S+)', re.IGNORECASE)
412     re_CharStyle = re.compile(b'^(\\s*)CharStyle(\\s+)(\\S+)$', re.IGNORECASE)
413     re_CiteFormat = re.compile(b'^(\\s*)(CiteFormat)(?:(\\s*)()|(\\s+)(default|authoryear|numerical))', re.IGNORECASE)
414     re_AMSMaths = re.compile(b'^\\s*Input ams(?:math|def)s.inc\\s*')
415     re_AMSMathsPlain = re.compile(b'^\\s*Input amsmaths-plain.inc\\s*')
416     re_AMSMathsSeq = re.compile(b'^\\s*Input amsmaths-seq.inc\\s*')
417     re_TocLevel = re.compile(b'^(\\s*)(TocLevel)(\\s+)(\\S+)', re.IGNORECASE)
418     re_I18nPreamble = re.compile(b'^(\\s*)I18nPreamble', re.IGNORECASE)
419     re_EndI18nPreamble = re.compile(b'^(\\s*)EndI18nPreamble', re.IGNORECASE)
420     re_Float = re.compile(b'^\\s*Float\\s*$', re.IGNORECASE)
421     re_Type = re.compile(b'\\s*Type\\s+(\\w+)', re.IGNORECASE)
422     re_Builtin = re.compile(b'^(\\s*)LaTeXBuiltin\\s+(\\w*)', re.IGNORECASE)
423     re_True = re.compile(b'^\\s*(?:true|1)\\s*$', re.IGNORECASE)
424     re_InsetLayout = re.compile(b'^\\s*InsetLayout\\s+(?:Custom|CharStyle|Element):(\\S+)\\s*$', re.IGNORECASE)
425     re_ResetsFont = re.compile(b'^(\\s*)ResetsFont(\\s+)(\\S+)$', re.IGNORECASE)
426     # with quotes
427     re_QInsetLayout = re.compile(b'^\\s*InsetLayout\\s+"(?:Custom|CharStyle|Element):([^"]+)"\\s*$', re.IGNORECASE)
428     re_InsetLayout_CopyStyle = re.compile(b'^\\s*CopyStyle\\s+(?:Custom|CharStyle|Element):(\\S+)\\s*$', re.IGNORECASE)
429     re_QInsetLayout_CopyStyle = re.compile(b'^\\s*CopyStyle\\s+"(?:Custom|CharStyle|Element):([^"]+)"\\s*$', re.IGNORECASE)
430     re_NeedsFloatPkg = re.compile(b'^(\\s*)NeedsFloatPkg\\s+(\\w+)\\s*$', re.IGNORECASE)
431     re_Fill = re.compile(b'^\\s*Fill_(?:Top|Bottom).*$', re.IGNORECASE)
432     re_InsetLayout2 = re.compile(b'^\\s*InsetLayout\\s+(\\S+)\\s*$', re.IGNORECASE)
433     # with quotes
434     re_QInsetLayout2 = re.compile(b'^\\s*InsetLayout\\s+"([^"]+)"\\s*$', re.IGNORECASE)
435     re_IsFlex = re.compile(b'\\s*LyXType.*$', re.IGNORECASE)
436     re_CopyStyle2 = re.compile(b'(\\s*CopyStyle\\s+)"?([^"]+)"?\\s*$')
437     re_Separator = re.compile(b'^(?:(-*)|(\\s*))(Separator|EndOfSlide)(?:(-*)|(\\s*))$', re.IGNORECASE)
438     # for categories
439     re_Declaration = re.compile(b'^#\\s*\\Declare\\w+Class.*$')
440     re_ExtractCategory = re.compile(b'^(#\\s*\\Declare\\w+Class(?:\\[[^]]*?\\])?){([^(]+?)\\s+\\(([^)]+?)\\)\\s*}\\s*$')
441     ConvDict = {"article": "Articles", "book" : "Books", "letter" : "Letters", "report": "Reports",
442                 "presentation" : "Presentations", "curriculum vitae" : "Curricula Vitae", "handout" : "Handouts"}
443     # Arguments
444     re_OptArgs = re.compile(b'^(\\s*)OptionalArgs(\\s+)(\\d+)\\D*$', re.IGNORECASE)
445     re_ReqArgs = re.compile(b'^(\\s*)RequiredArgs(\\s+)(\\d+)\\D*$', re.IGNORECASE)
446
447     # various changes associated with changing how chapters are handled
448     re_LabelTypeIsCounter = re.compile(b'^(\\s*)LabelType(\\s*)Counter\\s*$', re.IGNORECASE)
449     re_TopEnvironment = re.compile(b'^(\\s*)LabelType(\\s+)Top_Environment\\s*$', re.IGNORECASE)
450     re_CenteredEnvironment = re.compile(b'^(\\s*)LabelType(\\s+)Centered_Top_Environment\\s*$', re.IGNORECASE)
451     re_ChapterStyle = re.compile(b'^\\s*Style\\s+Chapter\\s*$', re.IGNORECASE)
452     re_InsetLayout_CaptionLTNN = re.compile(b'^(\\s*InsetLayout\\s+)(Caption:LongTableNonumber)', re.IGNORECASE)
453     # for format 64
454     re_trimLabelString = re.compile(b'^(\\s*LabelString\s+)"\\s*(.*?)\\s*"\\s*$')
455     re_trimLabelStringAppendix  = re.compile(b'^(\\s*LabelStringAppendix\s+)"\\s*(.*?)\\s*"\\s*$')
456     re_trimEndLabelString = re.compile(b'^(\\s*EndLabelString\s+)"\\s*(.*?)\\s*"\\s*$')
457     re_trimLabelCounter = re.compile(b'^(\\s*LabelCounter\s+)"\\s*(.*?)\\s*"\\s*$')
458
459
460     # counters for sectioning styles (hardcoded in 1.3)
461     counters = {b"part"          : b"\\Roman{part}",
462                 b"chapter"       : b"\\arabic{chapter}",
463                 b"section"       : b"\\arabic{section}",
464                 b"subsection"    : b"\\arabic{section}.\\arabic{subsection}",
465                 b"subsubsection" : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}",
466                 b"paragraph"     : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}.\\arabic{paragraph}",
467                 b"subparagraph"  : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}.\\arabic{paragraph}.\\arabic{subparagraph}"}
468
469     # counters for sectioning styles in appendix (hardcoded in 1.3)
470     appendixcounters = {b"chapter"       : b"\\Alph{chapter}",
471                         b"section"       : b"\\Alph{section}",
472                         b"subsection"    : b"\\arabic{section}.\\arabic{subsection}",
473                         b"subsubsection" : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}",
474                         b"paragraph"     : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}.\\arabic{paragraph}",
475                         b"subparagraph"  : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}.\\arabic{paragraph}.\\arabic{subparagraph}"}
476
477     # Value of TocLevel for sectioning styles
478     toclevels = {b"part"          : -1,
479                  b"chapter"       : 0,
480                  b"section"       : 1,
481                  b"subsection"    : 2,
482                  b"subsubsection" : 3,
483                  b"paragraph"     : 4,
484                  b"subparagraph"  : 5}
485
486     i = 0
487     only_comment = 1
488     counter = b""
489     toclevel = b""
490     label = b""
491     labelstring = b""
492     labelstringappendix = b""
493     space1 = b""
494     labelstring_line = -1
495     labelstringappendix_line = -1
496     labeltype_line = -1
497     latextype = b""
498     latextype_line = -1
499     style = b""
500     maxcounter = 0
501     format = 1
502     formatline = 0
503     usemodules = []
504     flexstyles = []
505     opts = 0
506     reqs = 0
507     inchapter = False
508     isflexlayout = False         # only used for 48 -> 49
509     # Whether a style is inherited (works only for CopyStyle currently,
510     # not for true inherited styles, see bug 8920
511     inherited = False        # only used for 48 -> 49
512     resetsfont_found = False # only used for 48 -> 49
513
514     while i < len(lines):
515         # Skip comments and empty lines
516         if (re_Comment.match(lines[i]) or re_Empty.match(lines[i])):
517           # We need to deal with this conversion here, because it happens
518           # inside the initial comment block.
519           if only_comment and format == 39:
520               match = re_ExtractCategory.match(lines[i])
521               if match:
522                   lpre = match.group(1)
523                   lcat = match.group(2)
524                   lnam = match.group(3)
525                   if lcat in ConvDict:
526                       lcat = ConvDict[lcat]
527                   lines[i] = lpre + b"{" + lnam + b"}"
528                   lines.insert(i+1, b"#  \\DeclareCategory{" + lcat + b"}")
529                   i += 1
530           i += 1
531           continue
532
533         # insert file format if not already there
534         if only_comment:
535             match = re_Format.match(lines[i])
536             if match:
537                 formatline = i
538                 format = int(match.group(4))
539                 if 1 < format < end_format:
540                     lines[i] = b"Format %d" % (format + 1)
541                     only_comment = 0
542                 elif format == end_format:
543                     # nothing to do
544                     return format
545                 else:
546                     error('Cannot convert file format %s to %s' % (format, end_format))
547             else:
548                 lines.insert(i, b"Format 2")
549                 only_comment = 0
550                 continue
551
552         # Don't get confused by LaTeX code
553         if re_Preamble.match(lines[i]):
554             i += 1
555             while i < len(lines) and not re_EndPreamble.match(lines[i]):
556                 i += 1
557             continue
558         if re_LangPreamble.match(lines[i]):
559             i += 1
560             while i < len(lines) and not re_EndLangPreamble.match(lines[i]):
561                 i += 1
562             continue
563         if re_BabelPreamble.match(lines[i]):
564             i += 1
565             while i < len(lines) and not re_EndBabelPreamble.match(lines[i]):
566                 i += 1
567             continue
568
569         if 87 <= format <= 93:
570             # nothing to do.
571             i += 1
572             continue
573
574         if format == 86:
575             if lines[i].lstrip().lower().startswith(b"stepmastercounter"):
576                 pattern = re.compile(b"stepmastercounter", re.IGNORECASE)
577                 lines[i] = pattern.sub(b"StepParentCounter", lines[i])
578             i += 1
579             continue
580
581         if 82 <= format <= 85:
582             # nothing to do.
583             i += 1
584             continue
585
586         if format == 81:
587             match = re.compile(b'^(\\s*Header\\s+)("?\\w+"?)', re.IGNORECASE).match(lines[i])
588             if match:
589                 del lines[i]
590                 continue
591
592             match = re.compile(b'(\\s*LyXType\\s+)(\\w+)(\\s*Element\\s+)', re.IGNORECASE).match(lines[i])
593             if match:
594                 del lines[i]
595                 continue
596
597             i += 1
598             continue
599
600         if 65 <= format <= 80:
601             # nothing to do.
602             i += 1
603             continue
604
605         if format == 64:
606             match = re.compile(b'(\\s*Color\\s+)(\\w+)', re.IGNORECASE).match(lines[i])
607             if not match:
608                 i += 1
609                 continue
610             col  = match.group(2)
611             if col == "collapsable":
612                 lines[i] = match.group(1) + "collapsible"
613             i += 1
614             continue
615
616         if format == 63:
617             for r in (re_trimLabelString, re_trimLabelStringAppendix,\
618               re_trimEndLabelString, re_trimLabelCounter):
619                 m = r.match(lines[i])
620                 if m:
621                     lines[i] = m.group(1) + b'"' + m.group(2) + b'"'
622             i += 1
623             continue
624
625         if 60 <= format <= 62:
626             # nothing to do.
627             i += 1
628             continue
629
630         if format == 59:
631             match = re_InsetLayout_CaptionLTNN.match(lines[i])
632             if not match:
633                 i += 1
634                 continue
635             # '^(\s*InsetLayout\s+)(Caption:LongTableNonumber)'
636             lead  = match.group(1)
637             lines[i] = lead + b"Caption:Unnumbered"
638             i += 1
639             continue
640
641         if format == 58:
642             # nothing to do.
643             i += 1
644             continue
645
646         if format == 57:
647             match = re_IfStyle.match(lines[i])
648             if not match:
649                 i += 1
650                 continue
651             # b'^(\\s*)IfStyle(\\s+\\S+)
652             lead  = match.group(1)
653             trail = match.group(2)
654             lines[i] = lead + b"ModifyStyle" + trail
655             i += 1
656             continue
657
658         if 50 <= format <= 56:
659             # nothing to do.
660             i += 1
661             continue
662
663         if format == 49:
664             separator = []
665
666             # delete separator styles
667             match = re_Style.match(lines[i])
668             if match:
669                 style = match.group(4).lower()
670                 if re_Separator.match(style):
671                     del lines[i]
672                     while i < len(lines) and not re_End.match(lines[i]):
673                         separator.append(lines[i])
674                         del lines[i]
675                     if i == len(lines):
676                         error('Incomplete separator style.')
677                     else:
678                         del lines[i]
679                         continue
680
681             # delete undefinition of separator styles
682             match = re_NoStyle.match(lines[i])
683             if match:
684                 style = match.group(4).lower()
685                 if re_Separator.match(style):
686                     del lines[i]
687                     continue
688
689             # replace the CopyStyle statement with the definition of the real
690             # style. This may result in duplicate statements, but that is OK
691             # since the second one will overwrite the first one.
692             match = re_CopyStyle.match(lines[i])
693             if match:
694                 style = match.group(4).lower()
695                 if re_Separator.match(style):
696                     if len(separator) > 0:
697                         lines[i:i+1] = separator
698                     else:
699                         # FIXME: If this style was redefined in an include file,
700                         # we should replace the real style and not this default.
701                         lines[i:i+1] = [b'      Category              MainText',
702                                         b'      KeepEmpty             1',
703                                         b'      Margin                Dynamic',
704                                         b'      LatexType             Paragraph',
705                                         b'      LatexName             dummy',
706                                         b'      ParIndent             MM',
707                                         b'      Align                 Block',
708                                         b'      LabelType             Static',
709                                         b'      LabelString           "--- Separate Environment ---"',
710                                         b'      LabelFont',
711                                         b'        Family              Roman',
712                                         b'        Series              Medium',
713                                         b'        Size                Normal',
714                                         b'        Color               Blue',
715                                         b'      EndFont',
716                                         b'      HTMLLabel             NONE']
717             i += 1
718             continue
719
720         if format == 48:
721             # The default of ResetsFont in LyX changed from true to false,
722             # because it is now used for all InsetLayouts, not only flex ones.
723             # Therefore we need to set it to true for all flex insets which do
724             # do not already have a ResetsFont.
725             match = re_InsetLayout2.match(lines[i])
726             if not match:
727                 i += 1
728                 continue
729
730             name = match.group(1).lower()
731             if name != b"flex" and name != b"\"flex\"" and name[0:5] != b"flex:" and name [0:6] != b"\"flex:":
732                 i += 1
733                 continue
734
735             resetsfont_found = False
736             inherited = False
737             notdone = True
738             while i < len(lines):
739               match = re_ResetsFont.match(lines[i])
740               if match:
741                   resetsfont_found = True
742               else:
743                 match = re_CopyStyle.match(lines[i])
744                 if match:
745                   inherited = True
746                 else:
747                   match = re_End.match(lines[i])
748                   if match:
749                     break
750               i += 1
751             if not resetsfont_found and not inherited:
752               lines.insert(i, b"\tResetsFont true")
753
754             continue
755
756         if 44 <= format <= 47:
757             # nothing to do.
758             i += 1
759             continue
760
761         if format == 43:
762           match = re_LabelTypeIsCounter.match(lines[i])
763           if match:
764             if inchapter:
765              lines[i] = match.group(1) + b"LabelType" + match.group(2) + b"Above"
766             else:
767               lines[i] = match.group(1) + b"LabelType" + match.group(2) + b"Static"
768
769           match = re_TopEnvironment.match(lines[i])
770           if match:
771             lines[i] = match.group(1) + b"LabelType" + match.group(2) + b"Above"
772
773           match = re_CenteredEnvironment.match(lines[i])
774           if match:
775             lines[i] = match.group(1) + b"LabelType" + match.group(2) + b"Centered"
776
777           if inchapter:
778             match = re_Style.match(lines[i])
779             if match:
780               inchapter = False
781           else:
782             match = re_ChapterStyle.match(lines[i])
783             if match:
784               inchapter = True
785
786           i += 1
787           continue
788
789         if format == 42:
790           if lines[i] == b"InsetLayout Caption":
791             lines[i] = b"InsetLayout Caption:Standard"
792           i += 1
793           continue
794
795         if format == 41:
796             # nothing to do.
797             i += 1
798             continue
799
800         if format == 40:
801             # reset counters on Style beginning
802             match = re_Style.match(lines[i])
803             if match:
804                 opts = 0
805                 reqs = 0
806                 i += 1
807                 continue
808             match = re_OptArgs.match(lines[i])
809             if match:
810                 # Save number of optional arguments
811                 space1 = match.group(1)
812                 opts = int(match.group(3))
813                 # OptionalArgs 0 > ResetArgs 1
814                 if opts == 0:
815                     lines[i] = space1 + b"ResetArgs\t1"
816                     i += 1
817                 else:
818                     del lines[i]
819                 continue
820             match = re_ReqArgs.match(lines[i])
821             if match:
822                 # Save number of required arguments
823                 space1 = match.group(1)
824                 reqs = int(match.group(3))
825                 del lines[i]
826                 continue
827             # Insert the required number of arguments at the end of the style definition
828             match = re_End.match(lines[i])
829             if match:
830                 newarg = ['']
831                 # First the optionals (this is the required order pre 2.1)
832                 if opts > 0:
833                     if opts == 1:
834                         newarg = [ b'%sArgument 1' % (space1),
835                                    b'%s\tLabelString\t\"Optional Layout Argument\"' % (space1),
836                                    b'%sEndArgument' % (space1)]
837                     elif opts > 1:
838                         actopt = 1
839                         while actopt < (opts + 1):
840                             newarg += [ b'%sArgument %d' % (space1, actopt),
841                                b'%s\tLabelString\t\"Optional Layout Argument %d\"' % (space1, actopt),
842                                b'%sEndArgument' % (space1)]
843                             actopt += 1
844                 # Now the mandatories
845                 if reqs > 0:
846                     actopt = opts + 1
847                     while actopt < (opts +  reqs + 1):
848                         newarg += [ b'%sArgument %d' % (space1, actopt),
849                            b'%s\tLabelString\t"Required Layout Argument %d"' % (space1, actopt - opts),
850                            b'%s\tMandatory\t1' % (space1),
851                            b'%sEndArgument' % (space1)]
852                         actopt += 1
853                 # Since we replace the "End" line, re-add this line
854                 if len(newarg) > 1:
855                     newarg += [b'End']
856                     lines[i:i+1] = newarg
857                     i += len(newarg)
858                 # Reset the counters
859                 opts = 0
860                 reqs = 0
861             i += 1
862             continue
863
864         if format == 39:
865             # There is a conversion with format 40, but it is done within the
866             # initial comment block and so is above.
867             i += 1
868             continue
869
870         if format == 37 or format == 38:
871             i += 1
872             continue
873
874         if format == 36:
875             match = re_CiteFormat.match(lines[i])
876             if match and match.group(4) == b"":
877                 lines[i] = match.group(0) + b" default"
878             i += 1
879             continue
880
881         if format == 35:
882           i += 1
883           continue
884
885         if format == 34:
886           match = re_QInsetLayout2.match(lines[i])
887           if not match:
888             match = re_InsetLayout2.match(lines[i])
889           if not match:
890             match = re_CopyStyle2.match(lines[i])
891             if not match:
892               i += 1
893               continue
894             style = match.group(2)
895
896             if flexstyles.count(style):
897               lines[i] = match.group(1) + b"\"Flex:" + style + b"\""
898             i += 1
899             continue
900
901           name = match.group(1)
902           names = name.split(b":", 1)
903           if len(names) > 1 and names[0] == b"Flex":
904             i += 1
905             continue
906
907           isflex = False
908           for j in range(i + 1, len(lines)):
909             if re_IsFlex.match(lines[j]):
910               isflex = True
911               break
912             if re_End.match(lines[j]):
913               break
914
915           if isflex:
916             flexstyles.append(name)
917             lines[i] = b"InsetLayout \"Flex:" + name + b"\""
918
919           i += 1
920           continue
921
922         if format == 33:
923           m = re_Fill.match(lines[i])
924           if m:
925             lines[i] = b""
926           i += 1
927           continue
928
929         if format == 32:
930           match = re_NeedsFloatPkg.match(lines[i])
931           if match:
932             space = match.group(1)
933             val = match.group(2)
934             lines[i] = space + b"UsesFloatPkg " + val
935             newval = b'true'
936             if val == b'1' or val.lower() == b'true':
937               newval = b'false'
938             lines.insert(i, space + b"IsPredefined " + newval)
939             i += 1
940           i += 1
941           continue
942
943         # Only new features
944         if 29 <= format <= 31:
945           i += 1
946           continue
947
948         if format == 28:
949           match = re_InsetLayout.match(lines[i])
950           if match:
951             lines[i] = b"InsetLayout Flex:" + match.group(1)
952           else:
953             match = re_QInsetLayout.match(lines[i])
954             if match:
955               lines[i] = b"InsetLayout \"Flex:" + match.group(1) + b"\""
956             else:
957               match = re_InsetLayout_CopyStyle.match(lines[i])
958               if match:
959                 lines[i] = b"\tCopyStyle Flex:" + match.group(1)
960               else:
961                 match = re_QInsetLayout_CopyStyle.match(lines[i])
962                 if match:
963                   lines[i] = b"\tCopyStyle \"Flex:" + match.group(1) + b"\""
964           i += 1
965           continue
966
967         # Only new features
968         if 24 <= format <= 27:
969           i += 1
970           continue
971
972         if format == 23:
973           match = re_Float.match(lines[i])
974           i += 1
975           if not match:
976             continue
977           # we need to do two things:
978           # (i)  Convert Builtin to NeedsFloatPkg
979           # (ii) Write ListCommand lines for the builtin floats table and figure
980           builtin = False
981           cmd = b""
982           while True and i < len(lines):
983             m1 = re_End.match(lines[i])
984             if m1:
985               if builtin and cmd:
986                 line = b"    ListCommand " + cmd
987                 lines.insert(i, line)
988                 i += 1
989               break
990             m2 = re_Builtin.match(lines[i])
991             if m2:
992               builtin = True
993               ws1 = m2.group(1)
994               arg = m2.group(2)
995               newarg = b""
996               if re_True.match(arg):
997                 newarg = b"false"
998               else:
999                 newarg = b"true"
1000               lines[i] = ws1 + b"NeedsFloatPkg " + newarg
1001             m3 = re_Type.match(lines[i])
1002             if m3:
1003               fltype = m3.group(1)
1004               fltype = fltype.lower()
1005               if fltype == b"table":
1006                 cmd = b"listoftables"
1007               elif fltype == b"figure":
1008                 cmd = b"listoffigures"
1009               # else unknown, which is why we're doing this
1010             i += 1
1011           continue
1012
1013         # This just involved new features, not any changes to old ones
1014         if 14 <= format <= 22:
1015           i += 1
1016           continue
1017
1018         # Rename I18NPreamble to BabelPreamble
1019         if format == 13:
1020             match = re_I18nPreamble.match(lines[i])
1021             if match:
1022                 lines[i] = match.group(1) + b"BabelPreamble"
1023                 i += 1
1024                 match = re_EndI18nPreamble.match(lines[i])
1025                 while i < len(lines) and not match:
1026                     i += 1
1027                     match = re_EndI18nPreamble.match(lines[i])
1028                 lines[i] = match.group(1) + b"EndBabelPreamble"
1029                 i += 1
1030                 continue
1031
1032         # These just involved new features, not any changes to old ones
1033         if format == 11 or format == 12:
1034           i += 1
1035           continue
1036
1037         if format == 10:
1038             match = re_UseMod.match(lines[i])
1039             if match:
1040                 module = match.group(1)
1041                 lines[i] = b"DefaultModule " + module
1042             i += 1
1043             continue
1044
1045         if format == 9:
1046             match = re_Counter.match(lines[i])
1047             if match:
1048                 counterline = i
1049                 i += 1
1050                 while i < len(lines):
1051                     namem = re_Name.match(lines[i])
1052                     if namem:
1053                         name = namem.group(1)
1054                         lines.pop(i)
1055                         lines[counterline] = b"Counter %s" % name
1056                         # we don't need to increment i
1057                         continue
1058                     endem = re_End.match(lines[i])
1059                     if endem:
1060                         i += 1
1061                         break
1062                     i += 1
1063             i += 1
1064             continue
1065
1066         if format == 8:
1067             # We want to scan for ams-type includes and, if we find them,
1068             # add corresponding UseModule tags to the layout.
1069             match = re_AMSMaths.match(lines[i])
1070             if match:
1071                 addstring(b"theorems-ams", usemodules)
1072                 addstring(b"theorems-ams-extended", usemodules)
1073                 addstring(b"theorems-sec", usemodules)
1074                 lines.pop(i)
1075                 continue
1076             match = re_AMSMathsPlain.match(lines[i])
1077             if match:
1078                 addstring(b"theorems-starred", usemodules)
1079                 lines.pop(i)
1080                 continue
1081             match = re_AMSMathsSeq.match(lines[i])
1082             if match:
1083                 addstring(b"theorems-ams", usemodules)
1084                 addstring(b"theorems-ams-extended", usemodules)
1085                 lines.pop(i)
1086                 continue
1087             i += 1
1088             continue
1089
1090         # These just involved new features, not any changes to old ones
1091         if 5 <= format <= 7:
1092           i += 1
1093           continue
1094
1095         if format == 4:
1096             # Handle conversion to long CharStyle names
1097             match = re_CharStyle.match(lines[i])
1098             if match:
1099                 lines[i] = b"InsetLayout CharStyle:%s" % (match.group(3))
1100                 i += 1
1101                 lines.insert(i, b"\tLyXType charstyle")
1102                 i += 1
1103                 lines.insert(i, b"")
1104                 lines[i] = b"\tLabelString %s" % (match.group(3))
1105             i += 1
1106             continue
1107
1108         if format == 3:
1109             # convert 'providesamsmath x',  'providesmakeidx x',  'providesnatbib x',  'providesurl x' to
1110             #         'provides amsmath x', 'provides makeidx x', 'provides natbib x', 'provides url x'
1111             # x is either 0 or 1
1112             match = re_Provides.match(lines[i])
1113             if match:
1114                 lines[i] = b"%sProvides %s%s%s" % (match.group(1), match.group(2).lower(),
1115                                                   match.group(3), match.group(4))
1116             i += 1
1117             continue
1118
1119         if format == 2:
1120             caption = []
1121
1122             # delete caption styles
1123             match = re_Style.match(lines[i])
1124             if match:
1125                 style = match.group(4).lower()
1126                 if style == b"caption":
1127                     del lines[i]
1128                     while i < len(lines) and not re_End.match(lines[i]):
1129                         caption.append(lines[i])
1130                         del lines[i]
1131                     if i == len(lines):
1132                         error('Incomplete caption style.')
1133                     else:
1134                         del lines[i]
1135                         continue
1136
1137             # delete undefinition of caption styles
1138             match = re_NoStyle.match(lines[i])
1139             if match:
1140                 style = match.group(4).lower()
1141                 if style == b"caption":
1142                     del lines[i]
1143                     continue
1144
1145             # replace the CopyStyle statement with the definition of the real
1146             # style. This may result in duplicate statements, but that is OK
1147             # since the second one will overwrite the first one.
1148             match = re_CopyStyle.match(lines[i])
1149             if match:
1150                 style = match.group(4).lower()
1151                 if style == b"caption":
1152                     if len(caption) > 0:
1153                         lines[i:i+1] = caption
1154                     else:
1155                         # FIXME: This style comes from an include file, we
1156                         # should replace the real style and not this default.
1157                         lines[i:i+1] = [b'      Margin                First_Dynamic',
1158                                         b'      LatexType             Command',
1159                                         b'      LatexName             caption',
1160                                         b'      NeedProtect           1',
1161                                         b'      LabelSep              xx',
1162                                         b'      ParSkip               0.4',
1163                                         b'      TopSep                0.5',
1164                                         b'      Align                 Center',
1165                                         b'      AlignPossible         Center',
1166                                         b'      LabelType             Sensitive',
1167                                         b'      LabelString           "Senseless!"',
1168                                         b'      OptionalArgs          1',
1169                                         b'      LabelFont',
1170                                         b'        Series              Bold',
1171                                         b'      EndFont']
1172
1173             i += 1
1174             continue
1175
1176         # Delete MaxCounter and remember the value of it
1177         match = re_MaxCounter.match(lines[i])
1178         if match:
1179             level = match.group(4).lower()
1180             if level == b"counter_chapter":
1181                 maxcounter = 0
1182             elif level == b"counter_section":
1183                 maxcounter = 1
1184             elif level == b"counter_subsection":
1185                 maxcounter = 2
1186             elif level == b"counter_subsubsection":
1187                 maxcounter = 3
1188             elif level == b"counter_paragraph":
1189                 maxcounter = 4
1190             elif level == b"counter_subparagraph":
1191                 maxcounter = 5
1192             elif level == b"counter_enumi":
1193                 maxcounter = 6
1194             elif level == b"counter_enumii":
1195                 maxcounter = 7
1196             elif level == b"counter_enumiii":
1197                 maxcounter = 8
1198             del lines[i]
1199             continue
1200
1201         # Replace line
1202         #
1203         # LabelType Counter_EnumI
1204         #
1205         # with two lines
1206         #
1207         # LabelType Counter
1208         # LabelCounter EnumI
1209         #
1210         match = re_LabelType.match(lines[i])
1211         if match:
1212             label = match.group(4)
1213             # Remember indenting space for later reuse in added lines
1214             space1 = match.group(1)
1215             # Remember the line for adding the LabelCounter later.
1216             # We can't do it here because it could shift latextype_line etc.
1217             labeltype_line = i
1218             if label[:8].lower() == b"counter_":
1219                 counter = label[8:].lower()
1220                 lines[i] = re_LabelType.sub(b'\\1\\2\\3Counter', lines[i])
1221
1222         # Remember the LabelString line
1223         match = re_LabelString.match(lines[i])
1224         if match:
1225             labelstring = match.group(4)
1226             labelstring_line = i
1227
1228         # Remember the LabelStringAppendix line
1229         match = re_LabelStringAppendix.match(lines[i])
1230         if match:
1231             labelstringappendix = match.group(4)
1232             labelstringappendix_line = i
1233
1234         # Remember the LatexType line
1235         match = re_LatexType.match(lines[i])
1236         if match:
1237             latextype = match.group(4).lower()
1238             latextype_line = i
1239
1240         # Remember the TocLevel line
1241         match = re_TocLevel.match(lines[i])
1242         if match:
1243             toclevel = match.group(4).lower()
1244
1245         # Reset variables at the beginning of a style definition
1246         match = re_Style.match(lines[i])
1247         if match:
1248             style = match.group(4).lower()
1249             counter = b""
1250             toclevel = b""
1251             label = b""
1252             space1 = b""
1253             labelstring = b""
1254             labelstringappendix = b""
1255             labelstring_line = -1
1256             labelstringappendix_line = -1
1257             labeltype_line = -1
1258             latextype = b""
1259             latextype_line = -1
1260
1261         if re_End.match(lines[i]):
1262
1263             # Add a line "LatexType Bib_Environment" if LabelType is Bibliography
1264             # (or change the existing LatexType)
1265             if label.lower() == b"bibliography":
1266                 if (latextype_line < 0):
1267                     lines.insert(i, b"%sLatexType Bib_Environment" % space1)
1268                     i += 1
1269                 else:
1270                     lines[latextype_line] = re_LatexType.sub(b'\\1\\2\\3Bib_Environment', lines[latextype_line])
1271
1272             # Change "LabelType Static" to "LabelType Itemize" for itemize environments
1273             if latextype == b"item_environment" and label.lower() == b"static":
1274                 lines[labeltype_line] = re_LabelType.sub(b'\\1\\2\\3Itemize', lines[labeltype_line])
1275
1276             # Change "LabelType Counter_EnumI" to "LabelType Enumerate" for enumerate environments
1277             if latextype == b"item_environment" and label.lower() == b"counter_enumi":
1278                 lines[labeltype_line] = re_LabelType.sub(b'\\1\\2\\3Enumerate', lines[labeltype_line])
1279                 # Don't add the LabelCounter line later
1280                 counter = ""
1281
1282             # Replace
1283             #
1284             # LabelString "Chapter"
1285             #
1286             # with
1287             #
1288             # LabelString "Chapter \arabic{chapter}"
1289             #
1290             # if this style has a counter. Ditto for LabelStringAppendix.
1291             # This emulates the hardcoded article style numbering of 1.3
1292             #
1293             if counter != b"":
1294                 if style in counters:
1295                     if labelstring_line < 0:
1296                         lines.insert(i, b'%sLabelString "%s"' % (space1, counters[style]))
1297                         i += 1
1298                     else:
1299                         new_labelstring = concatenate_label(labelstring, counters[style])
1300                         lines[labelstring_line] = re_LabelString.sub(
1301                                 b'\\1\\2\\3%s' % new_labelstring.replace(b"\\", b"\\\\"),
1302                                 lines[labelstring_line])
1303                 if style in appendixcounters:
1304                     if labelstringappendix_line < 0:
1305                         lines.insert(i, b'%sLabelStringAppendix "%s"' % (space1, appendixcounters[style]))
1306                         i += 1
1307                     else:
1308                         new_labelstring = concatenate_label(labelstring, appendixcounters[style])
1309                         lines[labelstringappendix_line] = re_LabelStringAppendix.sub(
1310                                 b'\\1\\2\\3%s' % new_labelstring.replace(b"\\", b"\\\\"),
1311                                 lines[labelstringappendix_line])
1312
1313                 # Now we can safely add the LabelCounter line
1314                 lines.insert(labeltype_line + 1, b"%sLabelCounter %s" % (space1, counter))
1315                 i += 1
1316
1317             # Add the TocLevel setting for sectioning styles
1318             if toclevel == b"" and style in toclevels and maxcounter <= toclevels[style]:
1319                 lines.insert(i, b'%s\tTocLevel %d' % (space1, toclevels[style]))
1320                 i += 1
1321
1322         i += 1
1323
1324     if only_comment:
1325         lines.insert(i, b"Format 2")
1326     if usemodules:
1327         i = formatline + 1
1328         for mod in usemodules:
1329             lines.insert(i, b"UseModule " + mod)
1330             i += 1
1331
1332     return format + 1
1333
1334
1335 def main(argv):
1336     args = {}
1337     args["description"] = "Convert layout file <inputfile> to a newer format."
1338
1339     parser = argparse.ArgumentParser(**args)
1340
1341     parser.add_argument("-t", "--to", type=int, dest="format", default= currentFormat,
1342                         help=("destination layout format, default %i (latest)") % currentFormat)
1343     parser.add_argument("input_file", nargs='?', type=cmd_arg, default=None,
1344                         help="input file (default stdin)")
1345     parser.add_argument("output_file", nargs='?', type=cmd_arg, default=None,
1346                         help="output file (default stdout)")
1347
1348     options = parser.parse_args(argv[1:])
1349
1350     # Open files
1351     if options.input_file:
1352         source = open(options.input_file, 'rb')
1353     elif PY2:
1354         source = sys.stdin
1355     else:
1356         source = sys.stdin.buffer
1357
1358     if options.output_file:
1359         output = open(options.output_file, 'wb')
1360     elif PY2:
1361         output = sys.stdout
1362     else:
1363         output = sys.stdout.buffer
1364
1365     if options.format > currentFormat:
1366         error("Format %i does not exist" % options.format);
1367
1368     # Do the real work
1369     lines = read(source)
1370     format = 1
1371     while (format < options.format):
1372         format = convert(lines, options.format)
1373     write(output, lines)
1374
1375     # Close files
1376     if options.input_file:
1377         source.close()
1378     if options.output_file:
1379         output.close()
1380
1381     return 0
1382
1383
1384 if __name__ == "__main__":
1385     main(sys.argv)