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