]> git.lyx.org Git - features.git/blob - lib/lyx2lyx/lyx_2_2.py
Remove redundant \\end_layout in jss-article Code Chunk reversion.
[features.git] / lib / lyx2lyx / lyx_2_2.py
1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # -*- coding: utf-8 -*-
4 # Copyright (C) 2015 The LyX team
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 """ Convert files to the file format generated by lyx 2.2"""
21
22 import re, string
23 import unicodedata
24 import sys, os
25
26 # Uncomment only what you need to import, please.
27
28 #from parser_tools import find_token, find_end_of, find_tokens, \
29 #  find_token_exact, find_end_of_inset, find_end_of_layout, \
30 #  find_token_backwards, is_in_inset, get_value, get_quoted_value, \
31 #  del_token, check_token, get_option_value
32
33 from lyx2lyx_tools import add_to_preamble, put_cmd_in_ert, get_ert, lyx2latex, \
34   lyx2verbatim, length_in_bp
35 #  insert_to_preamble, latex_length, revert_flex_inset, \
36 #  revert_font_attrs, hex2ratio, str2bool
37
38 from parser_tools import find_token, find_token_backwards, find_re, \
39      find_end_of_inset, find_end_of_layout, find_nonempty_line, \
40      get_containing_layout, get_value, check_token
41
42 ####################################################################
43 # Private helper functions
44
45 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt, nolastopt):
46     '''
47     Reverts an InsetArgument to TeX-code
48     usage:
49     revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt, notLastOpt)
50     LineOfBegin is the line  of the \begin_layout or \begin_inset statement
51     LineOfEnd is the line  of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
52     StartArgument is the number of the first argument that needs to be converted
53     EndArgument is the number of the last argument that needs to be converted or the last defined one
54     isEnvironment must be true, if the layout is for a LaTeX environment
55     isOpt must be true, if the argument is an optional one
56     notLastOpt must be true if the argument is mandatory and followed by optional ones
57     '''
58     lineArg = 0
59     wasOpt = False
60     while lineArg != -1 and n < nmax + 1:
61       lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
62       if lineArg > endline and endline != 0:
63         return wasOpt
64       if lineArg != -1:
65         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
66         # we have to assure that no other inset is in the Argument
67         beginInset = find_token(document.body, "\\begin_inset", beginPlain)
68         endInset = find_token(document.body, "\\end_inset", beginPlain)
69         k = beginPlain + 1
70         l = k
71         while beginInset < endInset and beginInset != -1:
72           beginInset = find_token(document.body, "\\begin_inset", k)
73           endInset = find_token(document.body, "\\end_inset", l)
74           k = beginInset + 1
75           l = endInset + 1
76         if environment == False:
77           if opt == False:
78             if nolastopt == False:
79               document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
80             else:
81               document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}") 
82             del(document.body[lineArg : beginPlain + 1])
83             wasOpt = False
84           else:
85             document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
86             document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
87             wasOpt = True
88         else:
89           if opt == False:
90             document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
91             document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
92             wasOpt = False
93           else:
94             document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
95             document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
96             wasOpt = True
97         n += 1
98     return wasOpt
99
100
101 ###############################################################################
102 ###
103 ### Conversion and reversion routines
104 ###
105 ###############################################################################
106
107 def convert_separator(document):
108     """
109     Convert layout separators to separator insets and add (LaTeX) paragraph
110     breaks in order to mimic previous LaTeX export.
111     """
112
113     parins = ["\\begin_inset Separator parbreak", "\\end_inset", ""]
114     parlay = ["\\begin_layout Standard", "\\begin_inset Separator parbreak",
115               "\\end_inset", "", "\\end_layout", ""]
116     sty_dict = {
117         "family" : "default",
118         "series" : "default",
119         "shape"  : "default",
120         "size"   : "default",
121         "bar"    : "default",
122         "color"  : "inherit"
123         }
124
125     i = 0
126     while 1:
127         i = find_token(document.body, "\\begin_deeper", i)
128         if i == -1:
129             break
130
131         j = find_token_backwards(document.body, "\\end_layout", i-1)
132         if j != -1:
133             # reset any text style before inserting the inset
134             lay = get_containing_layout(document.body, j-1)
135             if lay != False:
136                 content = "\n".join(document.body[lay[1]:lay[2]])
137                 for val in list(sty_dict.keys()):
138                     if content.find("\\%s" % val) != -1:
139                         document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
140                         i = i + 1
141                         j = j + 1
142             document.body[j:j] = parins
143             i = i + len(parins) + 1
144         else:
145             i = i + 1
146
147     i = 0
148     while 1:
149         i = find_token(document.body, "\\align", i)
150         if i == -1:
151             break
152
153         lay = get_containing_layout(document.body, i)
154         if lay != False and lay[0] == "Plain Layout":
155             i = i + 1
156             continue
157
158         j = find_token_backwards(document.body, "\\end_layout", i-1)
159         if j != -1:
160             lay = get_containing_layout(document.body, j-1)
161             if lay != False and lay[0] == "Standard" \
162                and find_token(document.body, "\\align", lay[1], lay[2]) == -1 \
163                and find_token(document.body, "\\begin_inset VSpace", lay[1], lay[2]) == -1:
164                 # reset any text style before inserting the inset
165                 content = "\n".join(document.body[lay[1]:lay[2]])
166                 for val in list(sty_dict.keys()):
167                     if content.find("\\%s" % val) != -1:
168                         document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
169                         i = i + 1
170                         j = j + 1
171                 document.body[j:j] = parins
172                 i = i + len(parins) + 1
173             else:
174                 i = i + 1
175         else:
176             i = i + 1
177
178     regexp = re.compile(r'^\\begin_layout (?:(-*)|(\s*))(Separator|EndOfSlide)(?:(-*)|(\s*))$', re.IGNORECASE)
179
180     i = 0
181     while 1:
182         i = find_re(document.body, regexp, i)
183         if i == -1:
184             return
185
186         j = find_end_of_layout(document.body, i)
187         if j == -1:
188             document.warning("Malformed LyX document: Missing `\\end_layout'.")
189             return
190
191         lay = get_containing_layout(document.body, j-1)
192         if lay != False:
193             lines = document.body[lay[3]:lay[2]]
194         else:
195             lines = []
196
197         document.body[i:j+1] = parlay
198         if len(lines) > 0:
199             document.body[i+1:i+1] = lines
200
201         i = i + len(parlay) + len(lines) + 1
202
203
204 def revert_separator(document):
205     " Revert separator insets to layout separators "
206
207     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
208     if document.textclass in beamer_classes:
209         beglaysep = "\\begin_layout Separator"
210     else:
211         beglaysep = "\\begin_layout --Separator--"
212
213     parsep = [beglaysep, "", "\\end_layout", ""]
214     comert = ["\\begin_inset ERT", "status collapsed", "",
215               "\\begin_layout Plain Layout", "%", "\\end_layout",
216               "", "\\end_inset", ""]
217     empert = ["\\begin_inset ERT", "status collapsed", "",
218               "\\begin_layout Plain Layout", " ", "\\end_layout",
219               "", "\\end_inset", ""]
220
221     i = 0
222     while 1:
223         i = find_token(document.body, "\\begin_inset Separator", i)
224         if i == -1:
225             return
226
227         lay = get_containing_layout(document.body, i)
228         if lay == False:
229             document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
230             i = i + 1
231             continue
232
233         layoutname = lay[0]
234         beg = lay[1]
235         end = lay[2]
236         kind = get_value(document.body, "\\begin_inset Separator", i, i+1, "plain").split()[1]
237         before = document.body[beg+1:i]
238         something_before = len(before) > 0 and len("".join(before)) > 0
239         j = find_end_of_inset(document.body, i)
240         after = document.body[j+1:end]
241         something_after = len(after) > 0 and len("".join(after)) > 0
242         if kind == "plain":
243             beg = beg + len(before) + 1
244         elif something_before:
245             document.body[i:i] = ["\\end_layout", ""]
246             i = i + 2
247             j = j + 2
248             beg = i
249             end = end + 2
250
251         if kind == "plain":
252             if something_after:
253                 document.body[beg:j+1] = empert
254                 i = i + len(empert)
255             else:
256                 document.body[beg:j+1] = comert
257                 i = i + len(comert)
258         else:
259             if something_after:
260                 if layoutname == "Standard":
261                     if not something_before:
262                         document.body[beg:j+1] = parsep
263                         i = i + len(parsep)
264                         document.body[i:i] = ["", "\\begin_layout Standard"]
265                         i = i + 2
266                     else:
267                         document.body[beg:j+1] = ["\\begin_layout Standard"]
268                         i = i + 1
269                 else:
270                     document.body[beg:j+1] = ["\\begin_deeper"]
271                     i = i + 1
272                     end = end + 1 - (j + 1 - beg)
273                     if not something_before:
274                         document.body[i:i] = parsep
275                         i = i + len(parsep)
276                         end = end + len(parsep)
277                     document.body[i:i] = ["\\begin_layout Standard"]
278                     document.body[end+2:end+2] = ["", "\\end_deeper", ""]
279                     i = i + 4
280             else:
281                 next_par_is_aligned = False
282                 k = find_nonempty_line(document.body, end+1)
283                 if k != -1 and check_token(document.body[k], "\\begin_layout"):
284                     lay = get_containing_layout(document.body, k)
285                     next_par_is_aligned = lay != False and \
286                             find_token(document.body, "\\align", lay[1], lay[2]) != -1
287                 if k != -1 and not next_par_is_aligned \
288                         and not check_token(document.body[k], "\\end_deeper") \
289                         and not check_token(document.body[k], "\\begin_deeper"):
290                     if layoutname == "Standard":
291                         document.body[beg:j+1] = [beglaysep]
292                         i = i + 1
293                     else:
294                         document.body[beg:j+1] = ["\\begin_deeper", beglaysep]
295                         end = end + 2 - (j + 1 - beg)
296                         document.body[end+1:end+1] = ["", "\\end_deeper", ""]
297                         i = i + 3
298                 else:
299                     if something_before:
300                         del document.body[i:end+1]
301                     else:
302                         del document.body[i:end-1]
303
304         i = i + 1
305
306
307 def revert_smash(document):
308     " Set amsmath to on if smash commands are used "
309
310     commands = ["smash[t]", "smash[b]", "notag"]
311     i = find_token(document.header, "\\use_package amsmath", 0)
312     if i == -1:
313         document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
314         return
315     value = get_value(document.header, "\\use_package amsmath", i).split()[1]
316     if value != "1":
317         # nothing to do if package is not auto but on or off
318         return
319     j = 0
320     while True:
321         j = find_token(document.body, '\\begin_inset Formula', j)
322         if j == -1:
323             return
324         k = find_end_of_inset(document.body, j)
325         if k == -1:
326             document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
327             j += 1
328             continue
329         code = "\n".join(document.body[j:k])
330         for c in commands:
331             if code.find("\\%s" % c) != -1:
332                 # set amsmath to on, since it is loaded by the newer format
333                 document.header[i] = "\\use_package amsmath 2"
334                 return
335         j = k
336
337
338 def revert_swissgerman(document):
339     " Set language german-ch-old to german "
340     i = 0
341     if document.language == "german-ch-old":
342         document.language = "german"
343         i = find_token(document.header, "\\language", 0)
344         if i != -1:
345             document.header[i] = "\\language german"
346     j = 0
347     while True:
348         j = find_token(document.body, "\\lang german-ch-old", j)
349         if j == -1:
350             return
351         document.body[j] = document.body[j].replace("\\lang german-ch-old", "\\lang german")
352         j = j + 1
353
354
355 def revert_use_package(document, pkg, commands, oldauto, supported):
356     # oldauto defines how the version we are reverting to behaves:
357     # if it is true, the old version uses the package automatically.
358     # if it is false, the old version never uses the package.
359     # If "supported" is true, the target version also supports this
360     # package natively.
361     regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
362     p = find_re(document.header, regexp, 0)
363     value = "1" # default is auto
364     if p != -1:
365         value = get_value(document.header, "\\use_package" , p).split()[1]
366         if not supported:
367             del document.header[p]
368     if value == "2" and not supported: # on
369         add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
370     elif value == "1" and not oldauto: # auto
371         i = 0
372         while True:
373             i = find_token(document.body, '\\begin_inset Formula', i)
374             if i == -1:
375                 return
376             j = find_end_of_inset(document.body, i)
377             if j == -1:
378                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
379                 i += 1
380                 continue
381             code = "\n".join(document.body[i:j])
382             for c in commands:
383                 if code.find("\\%s" % c) != -1:
384                     if supported:
385                         document.header[p] = "\\use_package " + pkg + " 2"
386                     else:
387                         add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
388                     return
389             i = j
390
391
392 mathtools_commands = ["xhookrightarrow", "xhookleftarrow", "xRightarrow", \
393                 "xrightharpoondown", "xrightharpoonup", "xrightleftharpoons", \
394                 "xLeftarrow", "xleftharpoondown", "xleftharpoonup", \
395                 "xleftrightarrow", "xLeftrightarrow", "xleftrightharpoons", \
396                 "xmapsto"]
397
398 def revert_xarrow(document):
399     "remove use_package mathtools"
400     revert_use_package(document, "mathtools", mathtools_commands, False, True)
401
402
403 def revert_beamer_lemma(document):
404     " Reverts beamer lemma layout to ERT "
405
406     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
407     if document.textclass not in beamer_classes:
408         return
409
410     consecutive = False
411     i = 0
412     while True:
413         i = find_token(document.body, "\\begin_layout Lemma", i)
414         if i == -1:
415             return
416         j = find_end_of_layout(document.body, i)
417         if j == -1:
418             document.warning("Malformed LyX document: Can't find end of Lemma layout")
419             i += 1
420             continue
421         arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
422         endarg1 = find_end_of_inset(document.body, arg1)
423         arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
424         endarg2 = find_end_of_inset(document.body, arg2)
425         subst1 = []
426         subst2 = []
427         if arg1 != -1:
428             beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1)
429             if beginPlain1 == -1:
430                 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
431                 i += 1
432                 continue
433             endPlain1 = find_end_of_inset(document.body, beginPlain1)
434             content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
435             subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
436         if arg2 != -1:
437             beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2)
438             if beginPlain2 == -1:
439                 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
440                 i += 1
441                 continue
442             endPlain2 = find_end_of_inset(document.body, beginPlain2)
443             content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
444             subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
445
446         # remove Arg insets
447         if arg1 < arg2:
448             del document.body[arg2 : endarg2 + 1]
449             if arg1 != -1:
450                 del document.body[arg1 : endarg1 + 1]
451         if arg2 < arg1:
452             del document.body[arg1 : endarg1 + 1]
453             if arg2 != -1:
454                 del document.body[arg2 : endarg2 + 1]
455
456         # index of end layout has probably changed
457         j = find_end_of_layout(document.body, i)
458         if j == -1:
459             document.warning("Malformed LyX document: Can't find end of Lemma layout")
460             i += 1
461             continue
462
463         begcmd = []
464
465         # if this is not a consecutive env, add start command
466         if not consecutive:
467             begcmd = put_cmd_in_ert("\\begin{lemma}")
468
469         # has this a consecutive lemma?
470         consecutive = document.body[j + 2] == "\\begin_layout Lemma"
471
472         # if this is not followed by a consecutive env, add end command
473         if not consecutive:
474             document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
475
476         document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
477
478         i = j
479
480
481
482 def revert_question_env(document):
483     """
484     Reverts question and question* environments of
485     theorems-ams-extended-bytype module to ERT
486     """
487
488     # Do we use theorems-ams-extended-bytype module?
489     if not "theorems-ams-extended-bytype" in document.get_module_list():
490         return
491
492     consecutive = False
493     i = 0
494     while True:
495         i = find_token(document.body, "\\begin_layout Question", i)
496         if i == -1:
497             return
498
499         starred = document.body[i] == "\\begin_layout Question*"
500
501         j = find_end_of_layout(document.body, i)
502         if j == -1:
503             document.warning("Malformed LyX document: Can't find end of Question layout")
504             i += 1
505             continue
506
507         # if this is not a consecutive env, add start command
508         begcmd = []
509         if not consecutive:
510             if starred:
511                 begcmd = put_cmd_in_ert("\\begin{question*}")
512             else:
513                 begcmd = put_cmd_in_ert("\\begin{question}")
514
515         # has this a consecutive theorem of same type?
516         consecutive = False
517         if starred:
518             consecutive = document.body[j + 2] == "\\begin_layout Question*"
519         else:
520             consecutive = document.body[j + 2] == "\\begin_layout Question"
521
522         # if this is not followed by a consecutive env, add end command
523         if not consecutive:
524             if starred:
525                 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
526             else:
527                 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
528
529         document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
530
531         add_to_preamble(document, "\\providecommand{\questionname}{Question}")
532
533         if starred:
534             add_to_preamble(document, "\\theoremstyle{plain}\n" \
535                                       "\\newtheorem*{question*}{\\protect\\questionname}")
536         else:
537             add_to_preamble(document, "\\theoremstyle{plain}\n" \
538                                       "\\newtheorem{question}{\\protect\\questionname}")
539
540         i = j
541
542
543 def convert_dashes(document):
544     "convert -- and --- to \\twohyphens and \\threehyphens"
545
546     if document.backend != "latex":
547         return
548
549     i = 0
550     while i < len(document.body):
551         words = document.body[i].split()
552         if len(words) > 1 and words[0] == "\\begin_inset" and \
553            words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
554             # must not replace anything in insets that store LaTeX contents in .lyx files
555             # (math and command insets withut overridden read() and write() methods
556             # filtering out IPA makes Text::readParToken() more simple
557             # skip ERT as well since it is not needed there
558             j = find_end_of_inset(document.body, i)
559             if j == -1:
560                 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
561                 i += 1
562             else:
563                 i = j
564             continue
565         while True:
566             j = document.body[i].find("--")
567             if j == -1:
568                 break
569             front = document.body[i][:j]
570             back = document.body[i][j+2:]
571             # We can have an arbitrary number of consecutive hyphens.
572             # These must be split into the corresponding number of two and three hyphens
573             # We must match what LaTeX does: First try emdash, then endash, then single hyphen
574             if back.find("-") == 0:
575                 back = back[1:]
576                 if len(back) > 0:
577                     document.body.insert(i+1, back)
578                 document.body[i] = front + "\\threehyphens"
579             else:
580                 if len(back) > 0:
581                     document.body.insert(i+1, back)
582                 document.body[i] = front + "\\twohyphens"
583         i += 1
584
585
586 def revert_dashes(document):
587     "convert \\twohyphens and \\threehyphens to -- and ---"
588
589     i = 0
590     while i < len(document.body):
591         words = document.body[i].split()
592         if len(words) > 1 and words[0] == "\\begin_inset" and \
593            words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
594             # see convert_dashes
595             j = find_end_of_inset(document.body, i)
596             if j == -1:
597                 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
598                 i += 1
599             else:
600                 i = j
601             continue
602         replaced = False
603         if document.body[i].find("\\twohyphens") >= 0:
604             document.body[i] = document.body[i].replace("\\twohyphens", "--")
605             replaced = True
606         if document.body[i].find("\\threehyphens") >= 0:
607             document.body[i] = document.body[i].replace("\\threehyphens", "---")
608             replaced = True
609         if replaced and i+1 < len(document.body) and \
610            (document.body[i+1].find("\\") != 0 or \
611             document.body[i+1].find("\\twohyphens") == 0 or
612             document.body[i+1].find("\\threehyphens") == 0) and \
613            len(document.body[i]) + len(document.body[i+1]) <= 80:
614             document.body[i] = document.body[i] + document.body[i+1]
615             document.body[i+1:i+2] = []
616         else:
617             i += 1
618
619
620 # order is important for the last three!
621 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
622
623 def is_part_of_converted_phrase(line, j, phrase):
624     "is phrase part of an already converted phrase?"
625     for p in phrases:
626         converted = "\\SpecialCharNoPassThru \\" + p
627         pos = j + len(phrase) - len(converted)
628         if pos >= 0:
629             if line[pos:pos+len(converted)] == converted:
630                 return True
631     return False
632
633
634 def convert_phrases(document):
635     "convert special phrases from plain text to \\SpecialCharNoPassThru"
636
637     if document.backend != "latex":
638         return
639
640     for phrase in phrases:
641         i = 0
642         while i < len(document.body):
643             words = document.body[i].split()
644             if len(words) > 1 and words[0] == "\\begin_inset" and \
645                words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
646                 # must not replace anything in insets that store LaTeX contents in .lyx files
647                 # (math and command insets withut overridden read() and write() methods
648                 j = find_end_of_inset(document.body, i)
649                 if j == -1:
650                     document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
651                     i += 1
652                 else:
653                     i = j
654                 continue
655             if document.body[i].find("\\") == 0:
656                 i += 1
657                 continue
658             j = document.body[i].find(phrase)
659             if j == -1:
660                 i += 1
661                 continue
662             if not is_part_of_converted_phrase(document.body[i], j, phrase):
663                 front = document.body[i][:j]
664                 back = document.body[i][j+len(phrase):]
665                 if len(back) > 0:
666                     document.body.insert(i+1, back)
667                 # We cannot use SpecialChar since we do not know whether we are outside passThru
668                 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
669             i += 1
670
671
672 def revert_phrases(document):
673     "convert special phrases to plain text"
674
675     i = 0
676     while i < len(document.body):
677         words = document.body[i].split()
678         if len(words) > 1 and words[0] == "\\begin_inset" and \
679            words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
680             # see convert_phrases
681             j = find_end_of_inset(document.body, i)
682             if j == -1:
683                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
684                 i += 1
685             else:
686                 i = j
687             continue
688         replaced = False
689         for phrase in phrases:
690             # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
691             if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
692                 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
693                 replaced = True
694             if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
695                 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
696                 replaced = True
697         if replaced and i+1 < len(document.body) and \
698            (document.body[i+1].find("\\") != 0 or \
699             document.body[i+1].find("\\SpecialChar") == 0) and \
700            len(document.body[i]) + len(document.body[i+1]) <= 80:
701             document.body[i] = document.body[i] + document.body[i+1]
702             document.body[i+1:i+2] = []
703             i -= 1
704         i += 1
705
706
707 def convert_specialchar_internal(document, forward):
708     specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
709         "\\@.":"endofsentence", "\\ldots{}":"ldots", \
710         "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
711         "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
712         "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
713         "\\LaTeX":"LaTeX" # must be after LaTeX2e
714     }
715
716     i = 0
717     while i < len(document.body):
718         words = document.body[i].split()
719         if len(words) > 1 and words[0] == "\\begin_inset" and \
720            words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
721             # see convert_phrases
722             j = find_end_of_inset(document.body, i)
723             if j == -1:
724                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
725                 i += 1
726             else:
727                 i = j
728             continue
729         for key, value in specialchars.iteritems():
730             if forward:
731                 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
732                 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
733             else:
734                 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
735                 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
736         i += 1
737
738
739 def convert_specialchar(document):
740     "convert special characters to new syntax"
741     convert_specialchar_internal(document, True)
742
743
744 def revert_specialchar(document):
745     "convert special characters to old syntax"
746     convert_specialchar_internal(document, False)
747
748
749 def revert_georgian(document):
750     "Set the document language to English but assure Georgian output"
751
752     if document.language == "georgian":
753         document.language = "english"
754         i = find_token(document.header, "\\language georgian", 0)
755         if i != -1:
756             document.header[i] = "\\language english"
757         j = find_token(document.header, "\\language_package default", 0)
758         if j != -1:
759             document.header[j] = "\\language_package babel"
760         k = find_token(document.header, "\\options", 0)
761         if k != -1:
762             document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
763         else:
764             l = find_token(document.header, "\\use_default_options", 0)
765             document.header.insert(l + 1, "\\options georgian")
766
767
768 def revert_sigplan_doi(document):
769     " Reverts sigplanconf DOI layout to ERT "
770
771     if document.textclass != "sigplanconf":
772         return
773
774     i = 0
775     while True:
776         i = find_token(document.body, "\\begin_layout DOI", i)
777         if i == -1:
778             return
779         j = find_end_of_layout(document.body, i)
780         if j == -1:
781             document.warning("Malformed LyX document: Can't find end of DOI layout")
782             i += 1
783             continue
784
785         content = lyx2latex(document, document.body[i:j + 1])
786         add_to_preamble(document, ["\\doi{" + content + "}"])
787         del document.body[i:j + 1]
788         # no need to reset i
789
790
791 def revert_ex_itemargs(document):
792     " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
793
794     if not "linguistics" in document.get_module_list():
795         return
796
797     i = 0
798     example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
799     while True:
800         i = find_token(document.body, "\\begin_inset Argument item:", i)
801         if i == -1:
802             return
803         j = find_end_of_inset(document.body, i)
804         # Find containing paragraph layout
805         parent = get_containing_layout(document.body, i)
806         if parent == False:
807             document.warning("Malformed LyX document: Can't find parent paragraph layout")
808             i += 1
809             continue
810         parbeg = parent[3]
811         layoutname = parent[0]
812         if layoutname in example_layouts:
813             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
814             endPlain = find_end_of_layout(document.body, beginPlain)
815             content = document.body[beginPlain + 1 : endPlain]
816             del document.body[i:j+1]
817             subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
818             document.body[parbeg : parbeg] = subst
819         i += 1
820
821
822 def revert_forest(document):
823     " Reverts the forest environment (Linguistics module) to TeX-code "
824
825     if not "linguistics" in document.get_module_list():
826         return
827
828     i = 0
829     while True:
830         i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
831         if i == -1:
832             return
833         j = find_end_of_inset(document.body, i)
834         if j == -1:
835             document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
836             i += 1
837             continue
838
839         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
840         endPlain = find_end_of_layout(document.body, beginPlain)
841         content = lyx2latex(document, document.body[beginPlain : endPlain])
842
843         add_to_preamble(document, ["\\usepackage{forest}"])
844
845         document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
846         # no need to reset i
847
848
849 def revert_glossgroup(document):
850     " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
851
852     if not "linguistics" in document.get_module_list():
853         return
854
855     i = 0
856     while True:
857         i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
858         if i == -1:
859             return
860         j = find_end_of_inset(document.body, i)
861         if j == -1:
862             document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
863             i += 1
864             continue
865
866         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
867         endPlain = find_end_of_layout(document.body, beginPlain)
868         content = lyx2verbatim(document, document.body[beginPlain : endPlain])
869
870         document.body[i:j + 1] = ["{", "", content, "", "}"]
871         # no need to reset i
872
873
874 def revert_newgloss(document):
875     " Reverts the new Glosse insets (Linguistics module) to the old format "
876
877     if not "linguistics" in document.get_module_list():
878         return
879
880     glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
881     for glosse in glosses:
882         i = 0
883         while True:
884             i = find_token(document.body, glosse, i)
885             if i == -1:
886                 break
887             j = find_end_of_inset(document.body, i)
888             if j == -1:
889                 document.warning("Malformed LyX document: Can't find end of Glosse inset")
890                 i += 1
891                 continue
892
893             arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
894             endarg = find_end_of_inset(document.body, arg)
895             argcontent = ""
896             if arg != -1:
897                 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
898                 if argbeginPlain == -1:
899                     document.warning("Malformed LyX document: Can't find arg plain Layout")
900                     i += 1
901                     continue
902                 argendPlain = find_end_of_inset(document.body, argbeginPlain)
903                 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
904
905                 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
906                     argcontent, "\\end_layout"]
907
908                 # remove Arg insets and paragraph, if it only contains this inset
909                 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
910                     del document.body[arg - 1 : endarg + 4]
911                 else:
912                     del document.body[arg : endarg + 1]
913
914             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
915             endPlain = find_end_of_layout(document.body, beginPlain)
916             content = lyx2verbatim(document, document.body[beginPlain : endPlain])
917
918             document.body[beginPlain + 1:endPlain] = [content]
919             i = beginPlain + 1
920
921     # Dissolve ERT insets
922     for glosse in glosses:
923         i = 0
924         while True:
925             i = find_token(document.body, "\\begin_inset ERT", i)
926             if i == -1:
927                 return
928             j = find_end_of_inset(document.body, i)
929             if j == -1:
930                 document.warning("Malformed LyX document: Can't find end of ERT inset")
931                 i += 1
932                 continue
933             ert = get_ert(document.body, i, True)
934             document.body[i:j+1] = [ert]
935             i = i + 1
936
937
938 def convert_newgloss(document):
939     " Converts Glosse insets (Linguistics module) to the new format "
940
941     if not "linguistics" in document.get_module_list():
942         return
943
944     glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
945     for glosse in glosses:
946         i = 0
947         while True:
948             i = find_token(document.body, glosse, i)
949             if i == -1:
950                 break
951             j = find_end_of_inset(document.body, i)
952             if j == -1:
953                 document.warning("Malformed LyX document: Can't find end of Glosse inset")
954                 i += 1
955                 continue
956
957             k = i
958             while True:
959                 argcontent = []
960                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
961                 if beginPlain == -1:
962                     break
963                 endPlain = find_end_of_layout(document.body, beginPlain)
964                 if endPlain == -1:
965                     document.warning("Malformed LyX document: Can't find end of Glosse layout")
966                     i += 1
967                     continue
968
969                 glt  = find_token(document.body, "\\backslash", beginPlain, endPlain)
970                 if glt != -1 and document.body[glt + 1].startswith("glt"):
971                     document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
972                     argcontent = document.body[glt + 1 : endPlain]
973                     document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
974                         "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
975                         "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
976                         "\\end_layout", "", "\\end_inset"]
977                 else:
978                     content = document.body[beginPlain + 1 : endPlain]
979                     document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
980                         "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
981
982                 endPlain = find_end_of_layout(document.body, beginPlain)
983                 k = endPlain
984                 j = find_end_of_inset(document.body, i)
985
986             i = endPlain + 1
987
988
989 def convert_BoxFeatures(document):
990     " adds new box features "
991
992     i = 0
993     while True:
994         i = find_token(document.body, "height_special", i)
995         if i == -1:
996             return
997         document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
998         i = i + 4
999
1000
1001 def revert_BoxFeatures(document):
1002     " outputs new box features as TeX code "
1003
1004     i = 0
1005     defaultSep = "3pt"
1006     defaultThick = "0.4pt"
1007     defaultShadow = "4pt"
1008     while True:
1009         i = find_token(document.body, "height_special", i)
1010         if i == -1:
1011             return
1012         # read out the values
1013         beg = document.body[i+1].find('"');
1014         end = document.body[i+1].rfind('"');
1015         thickness = document.body[i+1][beg+1:end];
1016         beg = document.body[i+2].find('"');
1017         end = document.body[i+2].rfind('"');
1018         separation = document.body[i+2][beg+1:end];
1019         beg = document.body[i+3].find('"');
1020         end = document.body[i+3].rfind('"');
1021         shadowsize = document.body[i+3][beg+1:end];
1022         # delete the specification
1023         del document.body[i+1:i+4]
1024         # output ERT
1025         # first output the closing brace
1026         if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1027             document.body[i + 10 : i + 10] = put_cmd_in_ert("}")
1028         # now output the lengths
1029         if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1030             document.body[i - 10 : i - 10] = put_cmd_in_ert("{")
1031         if thickness != defaultThick:
1032             document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness]
1033         if separation != defaultSep and thickness == defaultThick:
1034             document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation]
1035         if separation != defaultSep and thickness != defaultThick:
1036             document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1037         if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1038             document.body[i - 5 : i - 4] = ["{\\backslash shadowsize " + shadowsize]
1039         if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1040             document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1041         if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1042             document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1043         if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1044             document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1045         i = i + 11
1046
1047
1048 def convert_origin(document):
1049     " Insert the origin tag "
1050
1051     i = find_token(document.header, "\\textclass ", 0)
1052     if i == -1:
1053         document.warning("Malformed LyX document: No \\textclass!!")
1054         return
1055     if document.dir == "":
1056         origin = "stdin"
1057     else:
1058         relpath = ''
1059         if document.systemlyxdir and document.systemlyxdir != '':
1060             try:
1061                 if os.path.isabs(document.dir):
1062                     absdir = os.path.normpath(document.dir)
1063                 else:
1064                     absdir = os.path.normpath(os.path.abspath(document.dir))
1065                 if os.path.isabs(document.systemlyxdir):
1066                     abssys = os.path.normpath(document.systemlyxdir)
1067                 else:
1068                     abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1069                 relpath = os.path.relpath(absdir, abssys)
1070                 if relpath.find('..') == 0:
1071                     relpath = ''
1072             except:
1073                 relpath = ''
1074         if relpath == '':
1075             origin = document.dir.replace('\\', '/') + '/'
1076         else:
1077             origin = os.path.join("/systemlyxdir", relpath).replace('\\', '/') + '/'
1078         if os.name != 'nt':
1079             origin = unicode(origin, sys.getfilesystemencoding())
1080     document.header[i:i] = ["\\origin " + origin]
1081
1082
1083 def revert_origin(document):
1084     " Remove the origin tag "
1085
1086     i = find_token(document.header, "\\origin ", 0)
1087     if i == -1:
1088         document.warning("Malformed LyX document: No \\origin!!")
1089         return
1090     del document.header[i]
1091
1092
1093 color_names = ["brown", "darkgray", "gray", \
1094                "lightgray", "lime", "olive", "orange", \
1095                "pink", "purple", "teal", "violet"]
1096
1097 def revert_textcolor(document):
1098     " revert new \\textcolor colors to TeX code "
1099
1100     i = 0
1101     j = 0
1102     xcolor = False
1103     while True:
1104         i = find_token(document.body, "\\color ", i)
1105         if i == -1:
1106             return
1107         else:
1108             for color in list(color_names):
1109                 if document.body[i] == "\\color " + color:
1110                     # register that xcolor must be loaded in the preamble
1111                     if xcolor == False:
1112                         xcolor = True
1113                         add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1114                     # find the next \\color and/or the next \\end_layout
1115                     j = find_token(document.body, "\\color", i + 1)
1116                     k = find_token(document.body, "\\end_layout", i + 1)
1117                     if j == -1 and k != -1:
1118                         j = k +1 
1119                     # output TeX code
1120                     # first output the closing brace
1121                     if k < j:
1122                         document.body[k: k] = put_cmd_in_ert("}")
1123                     else:
1124                         document.body[j: j] = put_cmd_in_ert("}")
1125                     # now output the \textcolor command
1126                     document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1127         i = i + 1
1128
1129
1130 def convert_colorbox(document):
1131     " adds color settings for boxes "
1132
1133     i = 0
1134     while True:
1135         i = find_token(document.body, "shadowsize", i)
1136         if i == -1:
1137             return
1138         document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1139         i = i + 3
1140
1141
1142 def revert_colorbox(document):
1143     " outputs color settings for boxes as TeX code "
1144
1145     binset = 0
1146     defaultframecolor = "black"
1147     defaultbackcolor = "none"
1148     while True:
1149         binset = find_token(document.body, "\\begin_inset Box", binset)
1150         if binset == -1:
1151             return
1152
1153         einset = find_end_of_inset(document.body, binset)
1154         if einset == -1:
1155             document.warning("Malformed LyX document: Can't find end of box inset!")
1156             binset += 1
1157             continue
1158
1159         blay = find_token(document.body, "\\begin_layout", binset, einset)
1160         if blay == -1:
1161             document.warning("Malformed LyX document: Can't find start of layout!")
1162             binset = einset
1163             continue
1164
1165         # doing it this way, we make sure only to find a framecolor option
1166         frame = find_token(document.body, "framecolor", binset, blay)
1167         if frame == -1:
1168             binset = einset
1169             continue
1170
1171         beg = document.body[frame].find('"')
1172         end = document.body[frame].rfind('"')
1173         framecolor = document.body[frame][beg + 1 : end]
1174
1175         # this should be on the next line
1176         bgcolor = frame + 1
1177         beg = document.body[bgcolor].find('"')
1178         end = document.body[bgcolor].rfind('"')
1179         backcolor = document.body[bgcolor][beg + 1 : end]
1180
1181         # delete those bits
1182         del document.body[frame : frame + 2]
1183         # adjust end of inset
1184         einset -= 2
1185
1186         if document.body[binset] == "\\begin_inset Box Boxed" and \
1187             framecolor != defaultframecolor:
1188           document.body[binset] = "\\begin_inset Box Frameless"
1189
1190         # output TeX code
1191         # first output the closing brace
1192         if framecolor == defaultframecolor and backcolor == defaultbackcolor:
1193             # nothing needed
1194             pass
1195         else:
1196             # we also neeed to load xcolor in the preamble but only once
1197             add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1198             document.body[einset + 1 : einset + 1] = put_cmd_in_ert("}")
1199             if framecolor != defaultframecolor:
1200                 document.body[binset:binset] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1201             else:
1202               document.body[binset:binset] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1203
1204         binset = einset
1205
1206
1207 def revert_mathmulticol(document):
1208     " Convert formulas to ERT if they contain multicolumns "
1209
1210     i = 0
1211     while True:
1212         i = find_token(document.body, '\\begin_inset Formula', i)
1213         if i == -1:
1214             return
1215         j = find_end_of_inset(document.body, i)
1216         if j == -1:
1217             document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1218             i += 1
1219             continue
1220         lines = document.body[i:j]
1221         lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1222         code = "\n".join(lines)
1223         converted = False
1224         k = 0
1225         n = 0
1226         while n >= 0:
1227             n = code.find("\\multicolumn", k)
1228             # no need to convert degenerated multicolumn cells,
1229             # they work in old LyX versions as "math ERT"
1230             if n != -1 and code.find("\\multicolumn{1}", k) != n:
1231                 ert = put_cmd_in_ert(code)
1232                 document.body[i:j+1] = ert
1233                 converted = True
1234                 break
1235             else:
1236                 k = n + 12
1237         if converted:
1238             i = find_end_of_inset(document.body, i)
1239         else:
1240             i = j
1241
1242
1243 def revert_jss(document):
1244     " Reverts JSS In_Preamble commands to ERT in preamble "
1245
1246     if document.textclass != "jss":
1247         return
1248
1249     h = 0
1250     m = 0
1251     j = 0
1252     k = 0
1253     n = 0
1254     while True:
1255       # at first revert the inset layouts because they can be part of the In_Preamble layouts
1256       while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1257         # \pkg
1258         if h != -1:
1259           h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1260         if h != -1:
1261           endh = find_end_of_inset(document.body, h)
1262           document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1263           document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1264           h = h + 5
1265         # \proglang
1266         if m != -1:
1267           m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1268         if m != -1:
1269           endm = find_end_of_inset(document.body, m)
1270           document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1271           document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1272           m = m + 5
1273         # \code
1274         if j != -1:
1275           j = find_token(document.body, "\\begin_inset Flex Code", j)
1276         if j != -1:
1277           # assure that we are not in a Code Chunk inset
1278           if document.body[j][-1] == "e":
1279               endj = find_end_of_inset(document.body, j)
1280               document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1281               document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1282               j = j + 5
1283           else:
1284               j = j + 1
1285         # \email
1286         if k != -1:
1287           k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1288         if k != -1:
1289           endk = find_end_of_inset(document.body, k)
1290           document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1291           document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1292           k = k + 5
1293         # \url
1294         if n != -1:
1295           n = find_token(document.body, "\\begin_inset Flex URL", n)
1296         if n != -1:
1297           endn = find_end_of_inset(document.body, n)
1298           document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1299           document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1300           n = n + 5
1301       # now revert the In_Preamble layouts
1302       # \title
1303       i = find_token(document.body, "\\begin_layout Title", 0)
1304       if i == -1:
1305         return
1306       j = find_end_of_layout(document.body, i)
1307       if j == -1:
1308         document.warning("Malformed LyX document: Can't find end of Title layout")
1309         i += 1
1310         continue
1311       content = lyx2latex(document, document.body[i:j + 1])
1312       add_to_preamble(document, ["\\title{" + content + "}"])
1313       del document.body[i:j + 1]
1314       # \author
1315       i = find_token(document.body, "\\begin_layout Author", 0)
1316       if i == -1:
1317         return
1318       j = find_end_of_layout(document.body, i)
1319       if j == -1:
1320         document.warning("Malformed LyX document: Can't find end of Author layout")
1321         i += 1
1322         continue
1323       content = lyx2latex(document, document.body[i:j + 1])
1324       add_to_preamble(document, ["\\author{" + content + "}"])
1325       del document.body[i:j + 1]
1326       # \Plainauthor
1327       i = find_token(document.body, "\\begin_layout Plain Author", 0)
1328       if i == -1:
1329         return
1330       j = find_end_of_layout(document.body, i)
1331       if j == -1:
1332         document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1333         i += 1
1334         continue
1335       content = lyx2latex(document, document.body[i:j + 1])
1336       add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1337       del document.body[i:j + 1]
1338       # \Plaintitle
1339       i = find_token(document.body, "\\begin_layout Plain Title", 0)
1340       if i == -1:
1341         return
1342       j = find_end_of_layout(document.body, i)
1343       if j == -1:
1344         document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1345         i += 1
1346         continue
1347       content = lyx2latex(document, document.body[i:j + 1])
1348       add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1349       del document.body[i:j + 1]
1350       # \Shorttitle
1351       i = find_token(document.body, "\\begin_layout Short Title", 0)
1352       if i == -1:
1353         return
1354       j = find_end_of_layout(document.body, i)
1355       if j == -1:
1356         document.warning("Malformed LyX document: Can't find end of Short Title layout")
1357         i += 1
1358         continue
1359       content = lyx2latex(document, document.body[i:j + 1])
1360       add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1361       del document.body[i:j + 1]
1362       # \Abstract
1363       i = find_token(document.body, "\\begin_layout Abstract", 0)
1364       if i == -1:
1365         return
1366       j = find_end_of_layout(document.body, i)
1367       if j == -1:
1368         document.warning("Malformed LyX document: Can't find end of Abstract layout")
1369         i += 1
1370         continue
1371       content = lyx2latex(document, document.body[i:j + 1])
1372       add_to_preamble(document, ["\\Abstract{" + content + "}"])
1373       del document.body[i:j + 1]
1374       # \Keywords
1375       i = find_token(document.body, "\\begin_layout Keywords", 0)
1376       if i == -1:
1377         return
1378       j = find_end_of_layout(document.body, i)
1379       if j == -1:
1380         document.warning("Malformed LyX document: Can't find end of Keywords layout")
1381         i += 1
1382         continue
1383       content = lyx2latex(document, document.body[i:j + 1])
1384       add_to_preamble(document, ["\\Keywords{" + content + "}"])
1385       del document.body[i:j + 1]
1386       # \Plainkeywords
1387       i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1388       if i == -1:
1389         return
1390       j = find_end_of_layout(document.body, i)
1391       if j == -1:
1392         document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1393         i += 1
1394         continue
1395       content = lyx2latex(document, document.body[i:j + 1])
1396       add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1397       del document.body[i:j + 1]
1398       # \Address
1399       i = find_token(document.body, "\\begin_layout Address", 0)
1400       if i == -1:
1401         return
1402       j = find_end_of_layout(document.body, i)
1403       if j == -1:
1404         document.warning("Malformed LyX document: Can't find end of Address layout")
1405         i += 1
1406         continue
1407       content = lyx2latex(document, document.body[i:j + 1])
1408       add_to_preamble(document, ["\\Address{" + content + "}"])
1409       del document.body[i:j + 1]
1410       # finally handle the code layouts
1411       h = 0
1412       m = 0
1413       j = 0
1414       k = 0
1415       while m != -1 or j != -1 or h != -1 or k != -1:
1416         # \CodeChunk
1417         if h != -1:
1418           h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1419         if h != -1:
1420           endh = find_end_of_inset(document.body, h)
1421           document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1422           document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1423           document.body[h - 1 : h] = ["\\begin_layout Standard"]
1424           h = h + 1
1425         # \CodeInput
1426         if j != -1:
1427           j = find_token(document.body, "\\begin_layout Code Input", j)
1428         if j != -1:
1429           endj = find_end_of_layout(document.body, j)
1430           document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1431           document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1432           document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1433           document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1434           document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1435           j = j + 1
1436         # \CodeOutput
1437         if k != -1:
1438           k = find_token(document.body, "\\begin_layout Code Output", k)
1439         if k != -1:
1440           endk = find_end_of_layout(document.body, k)
1441           document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1442           document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1443           document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1444           document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1445           document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1446           k = k + 1
1447         # \Code
1448         if m != -1:
1449           m = find_token(document.body, "\\begin_layout Code", m)
1450         if m != -1:
1451           endm = find_end_of_layout(document.body, m)
1452           document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1453           document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1454           document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1455           document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1456           document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1457           m = m + 1
1458
1459
1460 def convert_subref(document):
1461     " converts sub: ref prefixes to subref: "
1462
1463     # 1) label insets
1464     rx = re.compile(r'^name \"sub:(.+)$')
1465     i = 0
1466     while True:
1467         i = find_token(document.body, "\\begin_inset CommandInset label", i)
1468         if i == -1:
1469             break
1470         j = find_end_of_inset(document.body, i)
1471         if j == -1:
1472             document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1473             i += 1
1474             continue
1475
1476         for p in range(i, j):
1477             m = rx.match(document.body[p])
1478             if m:
1479                 label = m.group(1)
1480                 document.body[p] = "name \"subsec:" + label
1481         i += 1
1482
1483     # 2) xref insets
1484     rx = re.compile(r'^reference \"sub:(.+)$')
1485     i = 0
1486     while True:
1487         i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1488         if i == -1:
1489             return
1490         j = find_end_of_inset(document.body, i)
1491         if j == -1:
1492             document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1493             i += 1
1494             continue
1495
1496         for p in range(i, j):
1497             m = rx.match(document.body[p])
1498             if m:
1499                 label = m.group(1)
1500                 document.body[p] = "reference \"subsec:" + label
1501                 break
1502         i += 1
1503
1504
1505
1506 def revert_subref(document):
1507     " reverts subref: ref prefixes to sub: "
1508
1509     # 1) label insets
1510     rx = re.compile(r'^name \"subsec:(.+)$')
1511     i = 0
1512     while True:
1513         i = find_token(document.body, "\\begin_inset CommandInset label", i)
1514         if i == -1:
1515             break
1516         j = find_end_of_inset(document.body, i)
1517         if j == -1:
1518             document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1519             i += 1
1520             continue
1521
1522         for p in range(i, j):
1523             m = rx.match(document.body[p])
1524             if m:
1525                 label = m.group(1)
1526                 document.body[p] = "name \"sub:" + label
1527                 break
1528         i += 1
1529
1530     # 2) xref insets
1531     rx = re.compile(r'^reference \"subsec:(.+)$')
1532     i = 0
1533     while True:
1534         i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1535         if i == -1:
1536             return
1537         j = find_end_of_inset(document.body, i)
1538         if j == -1:
1539             document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1540             i += 1
1541             continue
1542
1543         for p in range(i, j):
1544             m = rx.match(document.body[p])
1545             if m:
1546                 label = m.group(1)
1547                 document.body[p] = "reference \"sub:" + label
1548                 break
1549         i += 1
1550
1551
1552 def convert_nounzip(document):
1553     " remove the noUnzip parameter of graphics insets "
1554
1555     rx = re.compile(r'\s*noUnzip\s*$')
1556     i = 0
1557     while True:
1558         i = find_token(document.body, "\\begin_inset Graphics", i)
1559         if i == -1:
1560             break
1561         j = find_end_of_inset(document.body, i)
1562         if j == -1:
1563             document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1564             i += 1
1565             continue
1566
1567         k = find_re(document.body, rx, i, j)
1568         if k != -1:
1569           del document.body[k]
1570           j = j - 1
1571         i = j + 1
1572
1573
1574 def convert_revert_external_bbox(document, forward):
1575     " add units to bounding box of external insets "
1576
1577     rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1578     i = 0
1579     while True:
1580         i = find_token(document.body, "\\begin_inset External", i)
1581         if i == -1:
1582             break
1583         j = find_end_of_inset(document.body, i)
1584         if j == -1:
1585             document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1586             i += 1
1587             continue
1588         k = find_re(document.body, rx, i, j)
1589         if k == -1:
1590             i = j + 1
1591             continue
1592         tokens = document.body[k].split()
1593         if forward:
1594             for t in range(1, 5):
1595                 tokens[t] += "bp"
1596         else:
1597             for t in range(1, 5):
1598                 tokens[t] = length_in_bp(tokens[t])
1599         document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1600                            tokens[3] + " " + tokens[4]
1601         i = j + 1
1602
1603
1604 def convert_external_bbox(document):
1605     convert_revert_external_bbox(document, True)
1606
1607
1608 def revert_external_bbox(document):
1609     convert_revert_external_bbox(document, False)
1610
1611
1612 def revert_tcolorbox_1(document):
1613   " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1614   i = -1
1615   while True:
1616     i = find_token(document.header, "tcolorbox", i)
1617     if i == -1:
1618       break
1619     else:    
1620       flex = 0
1621       flexEnd = -1
1622       flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1623       if flex == -1:
1624         return flexEnd
1625       flexEnd = find_end_of_inset(document.body, flex)
1626       wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1627       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1628       flexEnd = find_end_of_inset(document.body, flex)
1629       if wasOpt == True:
1630         document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1631       else:
1632         document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1633       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1634       flex += 1
1635
1636
1637 def revert_tcolorbox_2(document):
1638   " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1639   i = -1
1640   while True:
1641     i = find_token(document.header, "tcolorbox", i)
1642     if i == -1:
1643       break
1644     else:    
1645       flex = 0
1646       flexEnd = -1
1647       flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1648       if flex == -1:
1649         return flexEnd
1650       flexEnd = find_end_of_inset(document.body, flex)
1651       revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1652       flexEnd = find_end_of_inset(document.body, flex)
1653       document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1654       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1655       flex += 1
1656
1657
1658 def revert_tcolorbox_3(document):
1659   " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1660   i = -1
1661   while True:
1662     i = find_token(document.header, "tcolorbox", i)
1663     if i == -1:
1664       break
1665     else:    
1666       flex = 0
1667       flexEnd = -1
1668       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1669       if flex == -1:
1670         return flexEnd
1671       flexEnd = find_end_of_inset(document.body, flex)
1672       revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1673       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1674       flexEnd = find_end_of_inset(document.body, flex)
1675       document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1676       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1677       flex += 1
1678
1679
1680 def revert_tcolorbox_4(document):
1681   " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1682   i = -1
1683   while True:
1684     i = find_token(document.header, "tcolorbox", i)
1685     if i == -1:
1686       break
1687     else:    
1688       flex = 0
1689       flexEnd = -1
1690       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1691       if flex == -1:
1692         return flexEnd
1693       flexEnd = find_end_of_inset(document.body, flex)
1694       revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1695       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1696       flexEnd = find_end_of_inset(document.body, flex)
1697       document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1698       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1699       flex += 1
1700
1701
1702 def revert_tcolorbox_5(document):
1703   " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1704   i = -1
1705   while True:
1706     i = find_token(document.header, "tcolorbox", i)
1707     if i == -1:
1708       break
1709     else:    
1710       flex = 0
1711       flexEnd = -1
1712       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1713       if flex == -1:
1714         return flexEnd
1715       flexEnd = find_end_of_inset(document.body, flex)
1716       revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1717       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1718       flexEnd = find_end_of_inset(document.body, flex)
1719       document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1720       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1721       flex += 1
1722
1723
1724 def revert_tcolorbox_6(document):
1725   " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1726   i = -1
1727   while True:
1728     i = find_token(document.header, "tcolorbox", i)
1729     if i == -1:
1730       break
1731     else:    
1732       flex = 0
1733       flexEnd = -1
1734       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1735       if flex == -1:
1736         return flexEnd
1737       flexEnd = find_end_of_inset(document.body, flex)
1738       revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1739       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1740       flexEnd = find_end_of_inset(document.body, flex)
1741       document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1742       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1743       flex += 1
1744
1745
1746 def revert_tcolorbox_7(document):
1747   " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1748   i = -1
1749   while True:
1750     i = find_token(document.header, "tcolorbox", i)
1751     if i == -1:
1752       break
1753     else:    
1754       flex = 0
1755       flexEnd = -1
1756       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1757       if flex == -1:
1758         return flexEnd
1759       flexEnd = find_end_of_inset(document.body, flex)
1760       revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1761       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1762       flexEnd = find_end_of_inset(document.body, flex)
1763       document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1764       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1765       flex += 1
1766
1767
1768 def revert_tcolorbox_8(document):
1769   " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1770   i = 0
1771   j = 0
1772   k = 0
1773   while True:
1774     if i != -1:
1775       i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1776     if i != -1:
1777       j = find_end_of_layout(document.body, i)
1778       wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1779       revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1780       revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1781       document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1782       if wasOpt == True:
1783         document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1784       else:
1785         document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1786       k = find_end_of_inset(document.body, j)
1787       k = find_token(document.body, "\\end_inset", k + 1)
1788       k = find_token(document.body, "\\end_inset", k + 1)
1789       if wasOpt == True:
1790         k = find_token(document.body, "\\end_inset", k + 1)
1791       document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1792       j = find_token(document.body, "\\begin_layout Standard", j + 1)
1793       document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1794       i += 1
1795     if i == -1:
1796       return
1797
1798
1799 def revert_moderncv_1(document):
1800   " Reverts the new inset of moderncv to TeX-code in preamble "
1801   
1802   if document.textclass != "moderncv":
1803     return
1804   i = 0
1805   j = 0
1806   lineArg = 0
1807   while True:
1808     # at first revert the new styles
1809     # \moderncvicons
1810     i = find_token(document.body, "\\begin_layout CVIcons", 0)
1811     if i == -1:
1812       return
1813     j = find_end_of_layout(document.body, i)
1814     if j == -1:
1815       document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1816       i += 1
1817       continue
1818     content = lyx2latex(document, document.body[i:j + 1])
1819     add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1820     del document.body[i:j + 1]
1821     # \hintscolumnwidth
1822     i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1823     if i == -1:
1824       return
1825     j = find_end_of_layout(document.body, i)
1826     if j == -1:
1827       document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1828       i += 1
1829       continue
1830     content = lyx2latex(document, document.body[i:j + 1])
1831     add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1832     del document.body[i:j + 1]
1833     # now change the new styles to the obsolete ones
1834     # \name
1835     i = find_token(document.body, "\\begin_layout Name", 0)
1836     if i == -1:
1837       return
1838     j = find_end_of_layout(document.body, i)
1839     if j == -1:
1840       document.warning("Malformed LyX document: Can't find end of Name layout")
1841       i += 1
1842       continue
1843     lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1844     if lineArg > j and j != 0:
1845       return
1846     if lineArg != -1:
1847       beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1848       # we have to assure that no other inset is in the Argument
1849       beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1850       endInset = find_token(document.body, "\\end_inset", beginPlain)
1851       k = beginPlain + 1
1852       l = k
1853       while beginInset < endInset and beginInset != -1:
1854         beginInset = find_token(document.body, "\\begin_inset", k)
1855         endInset = find_token(document.body, "\\end_inset", l)
1856         k = beginInset + 1
1857         l = endInset + 1
1858       Arg2 = document.body[l + 5 : l + 6]
1859       # rename the style
1860       document.body[i : i + 1]= ["\\begin_layout FirstName"]
1861       # delete the Argument inset
1862       del( document.body[endInset - 2 : endInset + 3])
1863       del( document.body[lineArg : beginPlain + 1])
1864       document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1865
1866
1867 def revert_moderncv_2(document):
1868   " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1869   
1870   if document.textclass != "moderncv":
1871     return
1872   i = 0
1873   j = 0
1874   lineArg = 0
1875   while True:
1876     # \phone
1877     i = find_token(document.body, "\\begin_layout Phone", i)
1878     if i == -1:
1879       return
1880     j = find_end_of_layout(document.body, i)
1881     if j == -1:
1882       document.warning("Malformed LyX document: Can't find end of Phone layout")
1883       i += 1
1884       return
1885     lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1886     if lineArg > j and j != 0:
1887       i += 1
1888       continue
1889     if lineArg != -1:
1890       beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1891       # we have to assure that no other inset is in the Argument
1892       beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1893       endInset = find_token(document.body, "\\end_inset", beginPlain)
1894       k = beginPlain + 1
1895       l = k
1896       while beginInset < endInset and beginInset != -1:
1897         beginInset = find_token(document.body, "\\begin_inset", k)
1898         endInset = find_token(document.body, "\\end_inset", l)
1899         k = beginInset + 1
1900         l = endInset + 1
1901       Arg = document.body[beginPlain + 1 : beginPlain + 2]
1902       # rename the style
1903       if Arg[0] == "mobile":
1904         document.body[i : i + 1]= ["\\begin_layout Mobile"]
1905       if Arg[0] == "fax":
1906         document.body[i : i + 1]= ["\\begin_layout Fax"]
1907       # delete the Argument inset
1908       del(document.body[endInset - 2 : endInset + 1])
1909       del(document.body[lineArg : beginPlain + 3])
1910     i += 1
1911
1912
1913 def convert_moderncv(document):
1914   " Convert the Fax and Mobile inset of moderncv to the new phone inset "
1915   
1916   if document.textclass != "moderncv":
1917     return
1918   i = 0
1919   j = 0
1920   lineArg = 0
1921   while True:
1922     # \mobile
1923     i = find_token(document.body, "\\begin_layout Mobile", i)
1924     if i == -1:
1925       return
1926     j = find_end_of_layout(document.body, i)
1927     if j == -1:
1928       document.warning("Malformed LyX document: Can't find end of Mobile layout")
1929       i += 1
1930       return
1931     document.body[i + 1 : i + 1] = ["\\begin_inset Argument 1", "status open", "",
1932                         "\\begin_layout Plain Layout", "mobile", "\\end_layout", "",
1933                         "\\end_inset", ""]
1934     # \fax
1935     i = find_token(document.body, "\\begin_layout Fax", i)
1936     if i == -1:
1937       return
1938     j = find_end_of_layout(document.body, i)
1939     if j == -1:
1940       document.warning("Malformed LyX document: Can't find end of Fax layout")
1941       i += 1
1942       return
1943     document.body[i + 1 : i + 1] = ["\\begin_inset Argument 1", "status open", "",
1944                         "\\begin_layout Plain Layout", "fax", "\\end_layout", "",
1945                         "\\end_inset", ""]
1946     # \firstname and \familyname
1947     i1 = find_token(document.body, "\\begin_layout FirstName", 0)
1948     if i1 == -1:
1949       return
1950     j1 = find_end_of_layout(document.body, i1)
1951     if j1 == -1:
1952       document.warning("Malformed LyX document: Can't find end of FirstName layout")
1953       i1 += 1
1954       return
1955     FirstName = document.body[i1 + 1 : i1 + 2]
1956     i2 = find_token(document.body, "\\begin_layout FamilyName", 0)
1957     if i2 == -1:
1958       return
1959     j2 = find_end_of_layout(document.body, i2)
1960     if j2 == -1:
1961       document.warning("Malformed LyX document: Can't find end of FamilyName layout")
1962       i2 += 1
1963       return
1964     FamilyName = document.body[i2 + 1 : i2 + 2]
1965     if j1 > j2:
1966       k = j1
1967       l = i2
1968     else:
1969       k = j2
1970       l = i1
1971     document.body[k + 1 : k + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
1972                         "\\begin_layout Plain Layout", FirstName[0], "\\end_layout", "",
1973                         "\\end_inset", "", FamilyName[0], "\\end_layout", ""]
1974     #document.body[i2 + 1 : i2 + 1] = ["hellok: ", str(k)]
1975     del(document.body[l : k])
1976     i += 1
1977     i1 += 1
1978     i2 += 1
1979
1980
1981 def revert_achemso(document):
1982   " Reverts the flex inset Latin to TeX code "
1983   
1984   if document.textclass != "achemso":
1985     return
1986   i = 0
1987   j = 0
1988   while True:
1989     i = find_token(document.body, "\\begin_inset Flex Latin", i)
1990     if i != -1:
1991       j = find_end_of_inset(document.body, i)
1992     else:
1993       return
1994     if j != -1:
1995       beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1996       endPlain = find_end_of_layout(document.body, beginPlain)
1997       content = lyx2latex(document, document.body[beginPlain : endPlain])
1998       document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
1999     else:
2000       document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2001       return
2002     i += 1
2003
2004
2005 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2006                 "\\font_sf_scale", "\\font_tt_scale"]
2007 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2008 fontquotes = [True, True, True, True, False, False]
2009
2010 def convert_fontsettings(document):
2011     " Duplicate font settings "
2012
2013     i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2014     if i == -1:
2015         document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2016         use_non_tex_fonts = "false"
2017     else:
2018         use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2019     j = 0
2020     for f in fontsettings:
2021         i = find_token(document.header, f + " ", 0)
2022         if i == -1:
2023             document.warning("Malformed LyX document: No " + f + "!")
2024             # we can fix that
2025             # note that with i = -1, this will insert at the end
2026             # of the header
2027             value = fontdefaults[j]
2028         else:
2029             value = document.header[i][len(f):].strip()
2030         if fontquotes[j]:
2031             if use_non_tex_fonts == "true":
2032                 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2033             else:
2034                 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2035         else:
2036             if use_non_tex_fonts == "true":
2037                 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2038             else:
2039                 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2040         j = j + 1
2041
2042
2043 def revert_fontsettings(document):
2044     " Merge font settings "
2045
2046     i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2047     if i == -1:
2048         document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2049         use_non_tex_fonts = "false"
2050     else:
2051         use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2052     j = 0
2053     for f in fontsettings:
2054         i = find_token(document.header, f + " ", 0)
2055         if i == -1:
2056             document.warning("Malformed LyX document: No " + f + "!")
2057             j = j + 1
2058             continue
2059         line = get_value(document.header, f, i)
2060         if fontquotes[j]:
2061             q1 = line.find('"')
2062             q2 = line.find('"', q1+1)
2063             q3 = line.find('"', q2+1)
2064             q4 = line.find('"', q3+1)
2065             if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2066                 document.warning("Malformed LyX document: Missing quotes!")
2067                 j = j + 1
2068                 continue
2069             if use_non_tex_fonts == "true":
2070                 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2071             else:
2072                 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2073         else:
2074             if use_non_tex_fonts == "true":
2075                 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2076             else:
2077                 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2078         j = j + 1
2079
2080
2081 def revert_solution(document):
2082     " Reverts the solution environment of the theorem module to TeX code "
2083
2084     # Do we use one of the modules that provides Solution?
2085     have_mod = False
2086     mods = document.get_module_list()
2087     for mod in mods:
2088         if mod == "theorems-std" or mod == "theorems-bytype" \
2089         or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2090             have_mod = True
2091             break
2092     if not have_mod:
2093         return
2094
2095     consecutive = False
2096     is_starred = False
2097     i = 0
2098     while True:
2099         i = find_token(document.body, "\\begin_layout Solution", i)
2100         if i == -1:
2101             return
2102
2103         is_starred = document.body[i].startswith("\\begin_layout Solution*")
2104         if is_starred == True:
2105             LaTeXName = "sol*"
2106             LyXName = "Solution*"
2107             theoremName = "newtheorem*"
2108         else:
2109             LaTeXName = "sol"
2110             LyXName = "Solution"
2111             theoremName = "newtheorem"
2112
2113         j = find_end_of_layout(document.body, i)
2114         if j == -1:
2115             document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2116             i += 1
2117             continue
2118
2119         # if this is not a consecutive env, add start command
2120         begcmd = []
2121         if not consecutive:
2122             begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2123
2124         # has this a consecutive theorem of same type?
2125         consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2126
2127         # if this is not followed by a consecutive env, add end command
2128         if not consecutive:
2129             document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2130
2131         document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2132
2133         add_to_preamble(document, "\\theoremstyle{definition}")
2134         if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2135             add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2136                 (theoremName, LaTeXName))
2137         else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2138             add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2139                 (theoremName, LaTeXName))
2140
2141         add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2142         i = j
2143
2144
2145 def revert_verbatim_star(document):
2146     from lyx_2_1 import revert_verbatim
2147     revert_verbatim(document, True)
2148
2149
2150 ##
2151 # Conversion hub
2152 #
2153
2154 supported_versions = ["2.2.0", "2.2"]
2155 convert = [
2156            [475, [convert_separator]],
2157            # nothing to do for 476: We consider it a bug that older versions
2158            # did not load amsmath automatically for these commands, and do not
2159            # want to hardcode amsmath off.
2160            [476, []],
2161            [477, []],
2162            [478, []],
2163            [479, []],
2164            [480, []],
2165            [481, [convert_dashes]],
2166            [482, [convert_phrases]],
2167            [483, [convert_specialchar]],
2168            [484, []],
2169            [485, []],
2170            [486, []],
2171            [487, []],
2172            [488, [convert_newgloss]],
2173            [489, [convert_BoxFeatures]],
2174            [490, [convert_origin]],
2175            [491, []],
2176            [492, [convert_colorbox]],
2177            [493, []],
2178            [494, []],
2179            [495, [convert_subref]],
2180            [496, [convert_nounzip]],
2181            [497, [convert_external_bbox]],
2182            [498, []],
2183            [499, [convert_moderncv]],
2184            [500, []],
2185            [501, [convert_fontsettings]],
2186            [502, []],
2187            [503, []]
2188           ]
2189
2190 revert =  [
2191            [502, [revert_verbatim_star]],
2192            [501, [revert_solution]],
2193            [500, [revert_fontsettings]],
2194            [499, [revert_achemso]],
2195            [498, [revert_moderncv_1, revert_moderncv_2]],
2196            [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2197                   revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2198                   revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2199            [496, [revert_external_bbox]],
2200            [495, []], # nothing to do since the noUnzip parameter was optional
2201            [494, [revert_subref]],
2202            [493, [revert_jss]],
2203            [492, [revert_mathmulticol]],
2204            [491, [revert_colorbox]],
2205            [490, [revert_textcolor]],
2206            [489, [revert_origin]],
2207            [488, [revert_BoxFeatures]],
2208            [487, [revert_newgloss, revert_glossgroup]],
2209            [486, [revert_forest]],
2210            [485, [revert_ex_itemargs]],
2211            [484, [revert_sigplan_doi]],
2212            [483, [revert_georgian]],
2213            [482, [revert_specialchar]],
2214            [481, [revert_phrases]],
2215            [480, [revert_dashes]],
2216            [479, [revert_question_env]],
2217            [478, [revert_beamer_lemma]],
2218            [477, [revert_xarrow]],
2219            [476, [revert_swissgerman]],
2220            [475, [revert_smash]],
2221            [474, [revert_separator]]
2222           ]
2223
2224
2225 if __name__ == "__main__":
2226     pass