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