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