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