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