]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_2.py
theorems-ams: add solution environment
[lyx.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, lyx2latex, \
34   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     have_mod = False
490     mods = document.get_module_list()
491     for mod in mods:
492         if mod == "theorems-ams-extended-bytype":
493             have_mod = True
494             continue
495
496     if not have_mod:
497         return
498
499     consecutive = False
500     i = 0
501     while True:
502         i = find_token(document.body, "\\begin_layout Question", i)
503         if i == -1:
504             return
505
506         starred = document.body[i] == "\\begin_layout Question*"
507
508         j = find_end_of_layout(document.body, i)
509         if j == -1:
510             document.warning("Malformed LyX document: Can't find end of Question layout")
511             i += 1
512             continue
513
514         # if this is not a consecutive env, add start command
515         begcmd = []
516         if not consecutive:
517             if starred:
518                 begcmd = put_cmd_in_ert("\\begin{question*}")
519             else:
520                 begcmd = put_cmd_in_ert("\\begin{question}")
521
522         # has this a consecutive theorem of same type?
523         consecutive = False
524         if starred:
525             consecutive = document.body[j + 2] == "\\begin_layout Question*"
526         else:
527             consecutive = document.body[j + 2] == "\\begin_layout Question"
528
529         # if this is not followed by a consecutive env, add end command
530         if not consecutive:
531             if starred:
532                 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
533             else:
534                 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
535
536         document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
537
538         add_to_preamble(document, "\\providecommand{\questionname}{Question}")
539
540         if starred:
541             add_to_preamble(document, "\\theoremstyle{plain}\n" \
542                                       "\\newtheorem*{question*}{\\protect\\questionname}")
543         else:
544             add_to_preamble(document, "\\theoremstyle{plain}\n" \
545                                       "\\newtheorem{question}{\\protect\\questionname}")
546
547         i = j
548
549
550 def convert_dashes(document):
551     "convert -- and --- to \\twohyphens and \\threehyphens"
552
553     if document.backend != "latex":
554         return
555
556     i = 0
557     while i < len(document.body):
558         words = document.body[i].split()
559         if len(words) > 1 and words[0] == "\\begin_inset" and \
560            words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
561             # must not replace anything in insets that store LaTeX contents in .lyx files
562             # (math and command insets withut overridden read() and write() methods
563             # filtering out IPA makes Text::readParToken() more simple
564             # skip ERT as well since it is not needed there
565             j = find_end_of_inset(document.body, i)
566             if j == -1:
567                 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
568                 i += 1
569             else:
570                 i = j
571             continue
572         while True:
573             j = document.body[i].find("--")
574             if j == -1:
575                 break
576             front = document.body[i][:j]
577             back = document.body[i][j+2:]
578             # We can have an arbitrary number of consecutive hyphens.
579             # These must be split into the corresponding number of two and three hyphens
580             # We must match what LaTeX does: First try emdash, then endash, then single hyphen
581             if back.find("-") == 0:
582                 back = back[1:]
583                 if len(back) > 0:
584                     document.body.insert(i+1, back)
585                 document.body[i] = front + "\\threehyphens"
586             else:
587                 if len(back) > 0:
588                     document.body.insert(i+1, back)
589                 document.body[i] = front + "\\twohyphens"
590         i += 1
591
592
593 def revert_dashes(document):
594     "convert \\twohyphens and \\threehyphens to -- and ---"
595
596     i = 0
597     while i < len(document.body):
598         words = document.body[i].split()
599         if len(words) > 1 and words[0] == "\\begin_inset" and \
600            words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
601             # see convert_dashes
602             j = find_end_of_inset(document.body, i)
603             if j == -1:
604                 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
605                 i += 1
606             else:
607                 i = j
608             continue
609         replaced = False
610         if document.body[i].find("\\twohyphens") >= 0:
611             document.body[i] = document.body[i].replace("\\twohyphens", "--")
612             replaced = True
613         if document.body[i].find("\\threehyphens") >= 0:
614             document.body[i] = document.body[i].replace("\\threehyphens", "---")
615             replaced = True
616         if replaced and i+1 < len(document.body) and \
617            (document.body[i+1].find("\\") != 0 or \
618             document.body[i+1].find("\\twohyphens") == 0 or
619             document.body[i+1].find("\\threehyphens") == 0) and \
620            len(document.body[i]) + len(document.body[i+1]) <= 80:
621             document.body[i] = document.body[i] + document.body[i+1]
622             document.body[i+1:i+2] = []
623         else:
624             i += 1
625
626
627 # order is important for the last three!
628 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
629
630 def is_part_of_converted_phrase(line, j, phrase):
631     "is phrase part of an already converted phrase?"
632     for p in phrases:
633         converted = "\\SpecialCharNoPassThru \\" + p
634         pos = j + len(phrase) - len(converted)
635         if pos >= 0:
636             if line[pos:pos+len(converted)] == converted:
637                 return True
638     return False
639
640
641 def convert_phrases(document):
642     "convert special phrases from plain text to \\SpecialCharNoPassThru"
643
644     if document.backend != "latex":
645         return
646
647     for phrase in phrases:
648         i = 0
649         while i < len(document.body):
650             words = document.body[i].split()
651             if len(words) > 1 and words[0] == "\\begin_inset" and \
652                words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
653                 # must not replace anything in insets that store LaTeX contents in .lyx files
654                 # (math and command insets withut overridden read() and write() methods
655                 j = find_end_of_inset(document.body, i)
656                 if j == -1:
657                     document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
658                     i += 1
659                 else:
660                     i = j
661                 continue
662             if document.body[i].find("\\") == 0:
663                 i += 1
664                 continue
665             j = document.body[i].find(phrase)
666             if j == -1:
667                 i += 1
668                 continue
669             if not is_part_of_converted_phrase(document.body[i], j, phrase):
670                 front = document.body[i][:j]
671                 back = document.body[i][j+len(phrase):]
672                 if len(back) > 0:
673                     document.body.insert(i+1, back)
674                 # We cannot use SpecialChar since we do not know whether we are outside passThru
675                 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
676             i += 1
677
678
679 def revert_phrases(document):
680     "convert special phrases to plain text"
681
682     i = 0
683     while i < len(document.body):
684         words = document.body[i].split()
685         if len(words) > 1 and words[0] == "\\begin_inset" and \
686            words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
687             # see convert_phrases
688             j = find_end_of_inset(document.body, i)
689             if j == -1:
690                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
691                 i += 1
692             else:
693                 i = j
694             continue
695         replaced = False
696         for phrase in phrases:
697             # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
698             if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
699                 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
700                 replaced = True
701             if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
702                 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
703                 replaced = True
704         if replaced and i+1 < len(document.body) and \
705            (document.body[i+1].find("\\") != 0 or \
706             document.body[i+1].find("\\SpecialChar") == 0) and \
707            len(document.body[i]) + len(document.body[i+1]) <= 80:
708             document.body[i] = document.body[i] + document.body[i+1]
709             document.body[i+1:i+2] = []
710             i -= 1
711         i += 1
712
713
714 def convert_specialchar_internal(document, forward):
715     specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
716         "\\@.":"endofsentence", "\\ldots{}":"ldots", \
717         "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
718         "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
719         "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
720         "\\LaTeX":"LaTeX" # must be after LaTeX2e
721     }
722
723     i = 0
724     while i < len(document.body):
725         words = document.body[i].split()
726         if len(words) > 1 and words[0] == "\\begin_inset" and \
727            words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
728             # see convert_phrases
729             j = find_end_of_inset(document.body, i)
730             if j == -1:
731                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
732                 i += 1
733             else:
734                 i = j
735             continue
736         for key, value in specialchars.iteritems():
737             if forward:
738                 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
739                 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
740             else:
741                 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
742                 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
743         i += 1
744
745
746 def convert_specialchar(document):
747     "convert special characters to new syntax"
748     convert_specialchar_internal(document, True)
749
750
751 def revert_specialchar(document):
752     "convert special characters to old syntax"
753     convert_specialchar_internal(document, False)
754
755
756 def revert_georgian(document):
757     "Set the document language to English but assure Georgian output"
758
759     if document.language == "georgian":
760         document.language = "english"
761         i = find_token(document.header, "\\language georgian", 0)
762         if i != -1:
763             document.header[i] = "\\language english"
764         j = find_token(document.header, "\\language_package default", 0)
765         if j != -1:
766             document.header[j] = "\\language_package babel"
767         k = find_token(document.header, "\\options", 0)
768         if k != -1:
769             document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
770         else:
771             l = find_token(document.header, "\\use_default_options", 0)
772             document.header.insert(l + 1, "\\options georgian")
773
774
775 def revert_sigplan_doi(document):
776     " Reverts sigplanconf DOI layout to ERT "
777
778     if document.textclass != "sigplanconf":
779         return
780
781     i = 0
782     while True:
783         i = find_token(document.body, "\\begin_layout DOI", i)
784         if i == -1:
785             return
786         j = find_end_of_layout(document.body, i)
787         if j == -1:
788             document.warning("Malformed LyX document: Can't find end of DOI layout")
789             i += 1
790             continue
791
792         content = lyx2latex(document, document.body[i:j + 1])
793         add_to_preamble(document, ["\\doi{" + content + "}"])
794         del document.body[i:j + 1]
795         # no need to reset i
796
797
798 def revert_ex_itemargs(document):
799     " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
800
801     # Do we use the linguistics module?
802     have_mod = False
803     mods = document.get_module_list()
804     for mod in mods:
805         if mod == "linguistics":
806             have_mod = True
807             continue
808
809     if not have_mod:
810         return
811
812     i = 0
813     example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
814     while True:
815         i = find_token(document.body, "\\begin_inset Argument item:", i)
816         if i == -1:
817             return
818         j = find_end_of_inset(document.body, i)
819         # Find containing paragraph layout
820         parent = get_containing_layout(document.body, i)
821         if parent == False:
822             document.warning("Malformed LyX document: Can't find parent paragraph layout")
823             i += 1
824             continue
825         parbeg = parent[3]
826         layoutname = parent[0]
827         if layoutname in example_layouts:
828             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
829             endPlain = find_end_of_layout(document.body, beginPlain)
830             content = document.body[beginPlain + 1 : endPlain]
831             del document.body[i:j+1]
832             subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
833             document.body[parbeg : parbeg] = subst
834         i += 1
835
836
837 def revert_forest(document):
838     " Reverts the forest environment (Linguistics module) to TeX-code "
839
840     # Do we use the linguistics module?
841     have_mod = False
842     mods = document.get_module_list()
843     for mod in mods:
844         if mod == "linguistics":
845             have_mod = True
846             continue
847
848     if not have_mod:
849         return
850
851     i = 0
852     while True:
853         i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
854         if i == -1:
855             return
856         j = find_end_of_inset(document.body, i)
857         if j == -1:
858             document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
859             i += 1
860             continue
861
862         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
863         endPlain = find_end_of_layout(document.body, beginPlain)
864         content = lyx2latex(document, document.body[beginPlain : endPlain])
865
866         add_to_preamble(document, ["\\usepackage{forest}"])
867
868         document.body[i:j + 1] = ["\\begin_inset ERT", "status collapsed", "",
869                 "\\begin_layout Plain Layout", "", "\\backslash", 
870                 "begin{forest}", "\\end_layout", "", "\\begin_layout Plain Layout",
871                 content, "\\end_layout", "", "\\begin_layout Plain Layout",
872                 "\\backslash", "end{forest}", "", "\\end_layout", "", "\\end_inset"]
873         # no need to reset i
874
875
876 def revert_glossgroup(document):
877     " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
878
879     # Do we use the linguistics module?
880     have_mod = False
881     mods = document.get_module_list()
882     for mod in mods:
883         if mod == "linguistics":
884             have_mod = True
885             continue
886
887     if not have_mod:
888         return
889
890     i = 0
891     while True:
892         i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
893         if i == -1:
894             return
895         j = find_end_of_inset(document.body, i)
896         if j == -1:
897             document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
898             i += 1
899             continue
900
901         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
902         endPlain = find_end_of_layout(document.body, beginPlain)
903         content = lyx2latex(document, document.body[beginPlain : endPlain])
904
905         document.body[i:j + 1] = ["{", "", content, "", "}"]
906         # no need to reset i
907
908
909 def revert_newgloss(document):
910     " Reverts the new Glosse insets (Linguistics module) to the old format "
911
912     # Do we use the linguistics module?
913     have_mod = False
914     mods = document.get_module_list()
915     for mod in mods:
916         if mod == "linguistics":
917             have_mod = True
918             continue
919
920     if not have_mod:
921         return
922
923     glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
924     for glosse in glosses:
925         i = 0
926         while True:
927             i = find_token(document.body, glosse, i)
928             if i == -1:
929                 break
930             j = find_end_of_inset(document.body, i)
931             if j == -1:
932                 document.warning("Malformed LyX document: Can't find end of Glosse inset")
933                 i += 1
934                 continue
935
936             arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
937             endarg = find_end_of_inset(document.body, arg)
938             argcontent = ""
939             if arg != -1:
940                 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
941                 if argbeginPlain == -1:
942                     document.warning("Malformed LyX document: Can't find arg plain Layout")
943                     i += 1
944                     continue
945                 argendPlain = find_end_of_inset(document.body, argbeginPlain)
946                 argcontent = lyx2latex(document, document.body[argbeginPlain : argendPlain - 2])
947
948                 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
949                     argcontent, "\\end_layout"]
950
951                 # remove Arg insets and paragraph, if it only contains this inset
952                 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
953                     del document.body[arg - 1 : endarg + 4]
954                 else:
955                     del document.body[arg : endarg + 1]
956
957             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
958             endPlain = find_end_of_layout(document.body, beginPlain)
959             content = lyx2latex(document, document.body[beginPlain : endPlain])
960
961             document.body[beginPlain + 1:endPlain] = [content]
962             i = beginPlain + 1
963
964
965 def convert_newgloss(document):
966     " Converts Glosse insets (Linguistics module) to the new format "
967
968     # Do we use the linguistics module?
969     have_mod = False
970     mods = document.get_module_list()
971     for mod in mods:
972         if mod == "linguistics":
973             have_mod = True
974             continue
975
976     if not have_mod:
977         return
978
979     glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
980     for glosse in glosses:
981         i = 0
982         while True:
983             i = find_token(document.body, glosse, i)
984             if i == -1:
985                 break
986             j = find_end_of_inset(document.body, i)
987             if j == -1:
988                 document.warning("Malformed LyX document: Can't find end of Glosse inset")
989                 i += 1
990                 continue
991
992             k = i
993             while True:
994                 argcontent = []
995                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
996                 if beginPlain == -1:
997                     break
998                 endPlain = find_end_of_layout(document.body, beginPlain)
999                 if endPlain == -1:
1000                     document.warning("Malformed LyX document: Can't find end of Glosse layout")
1001                     i += 1
1002                     continue
1003
1004                 glt  = find_token(document.body, "\\backslash", beginPlain, endPlain)
1005                 if glt != -1 and document.body[glt + 1].startswith("glt"):
1006                     document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1007                     argcontent = document.body[glt + 1 : endPlain]
1008                     document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1009                         "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1010                         "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1011                         "\\end_layout", "", "\\end_inset"]
1012                 else:
1013                     content = document.body[beginPlain + 1 : endPlain]
1014                     document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1015                         "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1016
1017                 endPlain = find_end_of_layout(document.body, beginPlain)
1018                 k = endPlain
1019                 j = find_end_of_inset(document.body, i)
1020
1021             i = endPlain + 1
1022
1023
1024 def convert_BoxFeatures(document):
1025     " adds new box features "
1026
1027     i = 0
1028     while True:
1029         i = find_token(document.body, "height_special", i)
1030         if i == -1:
1031             return
1032         document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1033         i = i + 4
1034
1035
1036 def revert_BoxFeatures(document):
1037     " outputs new box features as TeX code "
1038
1039     i = 0
1040     defaultSep = "3pt"
1041     defaultThick = "0.4pt"
1042     defaultShadow = "4pt"
1043     while True:
1044         i = find_token(document.body, "height_special", i)
1045         if i == -1:
1046             return
1047         # read out the values
1048         beg = document.body[i+1].find('"');
1049         end = document.body[i+1].rfind('"');
1050         thickness = document.body[i+1][beg+1:end];
1051         beg = document.body[i+2].find('"');
1052         end = document.body[i+2].rfind('"');
1053         separation = document.body[i+2][beg+1:end];
1054         beg = document.body[i+3].find('"');
1055         end = document.body[i+3].rfind('"');
1056         shadowsize = document.body[i+3][beg+1:end];
1057         # delete the specification
1058         del document.body[i+1:i+4]
1059         # output ERT
1060         # first output the closing brace
1061         if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1062             document.body[i + 10 : i + 10] = put_cmd_in_ert("}")
1063         # now output the lengths
1064         if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1065             document.body[i - 10 : i - 10] = put_cmd_in_ert("{")
1066         if thickness != defaultThick:
1067             document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness]
1068         if separation != defaultSep and thickness == defaultThick:
1069             document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation]
1070         if separation != defaultSep and thickness != defaultThick:
1071             document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1072         if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1073             document.body[i - 5 : i - 4] = ["{\\backslash shadowsize " + shadowsize]
1074         if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1075             document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1076         if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1077             document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1078         if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1079             document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1080         i = i + 11
1081
1082
1083 def convert_origin(document):
1084     " Insert the origin tag "
1085
1086     i = find_token(document.header, "\\textclass ", 0)
1087     if i == -1:
1088         document.warning("Malformed LyX document: No \\textclass!!")
1089         return;
1090     if document.dir == "":
1091         origin = "stdin"
1092     else:
1093         relpath = ''
1094         if document.systemlyxdir and document.systemlyxdir != '':
1095             try:
1096                 if os.path.isabs(document.dir):
1097                     absdir = os.path.normpath(document.dir)
1098                 else:
1099                     absdir = os.path.normpath(os.path.abspath(document.dir))
1100                 if os.path.isabs(document.systemlyxdir):
1101                     abssys = os.path.normpath(document.systemlyxdir)
1102                 else:
1103                     abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1104                 relpath = os.path.relpath(absdir, abssys)
1105                 if relpath.find('..') == 0:
1106                     relpath = ''
1107             except:
1108                 relpath = ''
1109         if relpath == '':
1110             origin = document.dir.replace('\\', '/') + '/'
1111         else:
1112             origin = os.path.join("/systemlyxdir", relpath).replace('\\', '/') + '/'
1113         if os.name != 'nt':
1114             origin = unicode(origin, sys.getfilesystemencoding())
1115     document.header[i:i] = ["\\origin " + origin]
1116
1117
1118 def revert_origin(document):
1119     " Remove the origin tag "
1120
1121     i = find_token(document.header, "\\origin ", 0)
1122     if i == -1:
1123         document.warning("Malformed LyX document: No \\origin!!")
1124         return;
1125     del document.header[i]
1126
1127
1128 color_names = ["brown", "darkgray", "gray", \
1129                "lightgray", "lime", "olive", "orange", \
1130                "pink", "purple", "teal", "violet"]
1131
1132 def revert_textcolor(document):
1133     " revert new \\textcolor colors to TeX code "
1134
1135     i = 0
1136     j = 0
1137     xcolor = False
1138     while True:
1139         i = find_token(document.body, "\\color ", i)
1140         if i == -1:
1141             return
1142         else:
1143             for color in list(color_names):
1144                 if document.body[i] == "\\color " + color:
1145                     # register that xcolor must be loaded in the preamble
1146                     if xcolor == False:
1147                         xcolor = True
1148                         add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1149                     # find the next \\color and/or the next \\end_layout
1150                     j = find_token(document.body, "\\color", i + 1)
1151                     k = find_token(document.body, "\\end_layout", i + 1)
1152                     if j == -1 and k != -1:
1153                         j = k +1 
1154                     # output TeX code
1155                     # first output the closing brace
1156                     if k < j:
1157                         document.body[k: k] = put_cmd_in_ert("}")
1158                     else:
1159                         document.body[j: j] = put_cmd_in_ert("}")
1160                     # now output the \textcolor command
1161                     document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1162         i = i + 1
1163
1164
1165 def convert_colorbox(document):
1166     " adds color settings for boxes "
1167
1168     i = 0
1169     while True:
1170         i = find_token(document.body, "shadowsize", i)
1171         if i == -1:
1172             return
1173         document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1174         i = i + 3
1175
1176
1177 def revert_colorbox(document):
1178     " outputs color settings for boxes as TeX code "
1179
1180     binset = 0
1181     defaultframecolor = "black"
1182     defaultbackcolor = "none"
1183     while True:
1184         binset = find_token(document.body, "\\begin_inset Box", binset)
1185         if binset == -1:
1186             return
1187
1188         einset = find_end_of_inset(document.body, binset)
1189         if einset == -1:
1190             document.warning("Malformed LyX document: Can't find end of box inset!")
1191             binset += 1
1192             continue
1193
1194         blay = find_token(document.body, "\\begin_layout", binset, einset)
1195         if blay == -1:
1196             document.warning("Malformed LyX document: Can't find start of layout!")
1197             binset = einset
1198             continue
1199
1200         # doing it this way, we make sure only to find a framecolor option
1201         frame = find_token(document.body, "framecolor", binset, blay)
1202         if frame == -1:
1203             binset = einset
1204             continue
1205
1206         beg = document.body[frame].find('"')
1207         end = document.body[frame].rfind('"')
1208         framecolor = document.body[frame][beg + 1 : end]
1209
1210         # this should be on the next line
1211         bgcolor = frame + 1
1212         beg = document.body[bgcolor].find('"')
1213         end = document.body[bgcolor].rfind('"')
1214         backcolor = document.body[bgcolor][beg + 1 : end]
1215
1216         # delete those bits
1217         del document.body[frame : frame + 2]
1218         # adjust end of inset
1219         einset -= 2
1220
1221         if document.body[binset] == "\\begin_inset Box Boxed" and \
1222             framecolor != defaultframecolor:
1223           document.body[binset] = "\\begin_inset Box Frameless"
1224
1225         # output TeX code
1226         # first output the closing brace
1227         if framecolor == defaultframecolor and backcolor == defaultbackcolor:
1228             # nothing needed
1229             pass
1230         else:
1231             # we also neeed to load xcolor in the preamble but only once
1232             add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1233             document.body[einset + 1 : einset + 1] = put_cmd_in_ert("}")
1234             if framecolor != defaultframecolor:
1235                 document.body[binset:binset] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1236             else:
1237               document.body[binset:binset] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1238
1239         binset = einset
1240
1241
1242 def revert_mathmulticol(document):
1243     " Convert formulas to ERT if they contain multicolumns "
1244
1245     i = 0
1246     while True:
1247         i = find_token(document.body, '\\begin_inset Formula', i)
1248         if i == -1:
1249             return
1250         j = find_end_of_inset(document.body, i)
1251         if j == -1:
1252             document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1253             i += 1
1254             continue
1255         lines = document.body[i:j]
1256         lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1257         code = "\n".join(lines)
1258         converted = False
1259         k = 0
1260         n = 0
1261         while n >= 0:
1262             n = code.find("\\multicolumn", k)
1263             # no need to convert degenerated multicolumn cells,
1264             # they work in old LyX versions as "math ERT"
1265             if n != -1 and code.find("\\multicolumn{1}", k) != n:
1266                 ert = put_cmd_in_ert(code)
1267                 document.body[i:j+1] = ert
1268                 converted = True
1269                 break
1270             else:
1271                 k = n + 12
1272         if converted:
1273             i = find_end_of_inset(document.body, i)
1274         else:
1275             i = j
1276
1277
1278 def revert_jss(document):
1279     " Reverts JSS In_Preamble commands to ERT in preamble "
1280
1281     if document.textclass != "jss":
1282         return
1283
1284     h = 0
1285     m = 0
1286     j = 0
1287     k = 0
1288     n = 0
1289     while True:
1290       # at first revert the inset layouts because they can be part of the In_Preamble layouts
1291       while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1292         # \pkg
1293         if h != -1:
1294           h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1295         if h != -1:
1296           endh = find_end_of_inset(document.body, h)
1297           document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1298           document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1299           h = h + 5
1300         # \proglang
1301         if m != -1:
1302           m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1303         if m != -1:
1304           endm = find_end_of_inset(document.body, m)
1305           document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1306           document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1307           m = m + 5
1308         # \code
1309         if j != -1:
1310           j = find_token(document.body, "\\begin_inset Flex Code", j)
1311         if j != -1:
1312           # assure that we are not in a Code Chunk inset
1313           if document.body[j][-1] == "e":
1314               endj = find_end_of_inset(document.body, j)
1315               document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1316               document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1317               j = j + 5
1318           else:
1319               j = j + 1
1320         # \email
1321         if k != -1:
1322           k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1323         if k != -1:
1324           endk = find_end_of_inset(document.body, k)
1325           document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1326           document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1327           k = k + 5
1328         # \url
1329         if n != -1:
1330           n = find_token(document.body, "\\begin_inset Flex URL", n)
1331         if n != -1:
1332           endn = find_end_of_inset(document.body, n)
1333           document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1334           document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1335           n = n + 5
1336       # now revert the In_Preamble layouts
1337       # \title
1338       i = find_token(document.body, "\\begin_layout Title", 0)
1339       if i == -1:
1340         return
1341       j = find_end_of_layout(document.body, i)
1342       if j == -1:
1343         document.warning("Malformed LyX document: Can't find end of Title layout")
1344         i += 1
1345         continue
1346       content = lyx2latex(document, document.body[i:j + 1])
1347       add_to_preamble(document, ["\\title{" + content + "}"])
1348       del document.body[i:j + 1]
1349       # \author
1350       i = find_token(document.body, "\\begin_layout Author", 0)
1351       if i == -1:
1352         return
1353       j = find_end_of_layout(document.body, i)
1354       if j == -1:
1355         document.warning("Malformed LyX document: Can't find end of Author layout")
1356         i += 1
1357         continue
1358       content = lyx2latex(document, document.body[i:j + 1])
1359       add_to_preamble(document, ["\\author{" + content + "}"])
1360       del document.body[i:j + 1]
1361       # \Plainauthor
1362       i = find_token(document.body, "\\begin_layout Plain Author", 0)
1363       if i == -1:
1364         return
1365       j = find_end_of_layout(document.body, i)
1366       if j == -1:
1367         document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1368         i += 1
1369         continue
1370       content = lyx2latex(document, document.body[i:j + 1])
1371       add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1372       del document.body[i:j + 1]
1373       # \Plaintitle
1374       i = find_token(document.body, "\\begin_layout Plain Title", 0)
1375       if i == -1:
1376         return
1377       j = find_end_of_layout(document.body, i)
1378       if j == -1:
1379         document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1380         i += 1
1381         continue
1382       content = lyx2latex(document, document.body[i:j + 1])
1383       add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1384       del document.body[i:j + 1]
1385       # \Shorttitle
1386       i = find_token(document.body, "\\begin_layout Short Title", 0)
1387       if i == -1:
1388         return
1389       j = find_end_of_layout(document.body, i)
1390       if j == -1:
1391         document.warning("Malformed LyX document: Can't find end of Short Title layout")
1392         i += 1
1393         continue
1394       content = lyx2latex(document, document.body[i:j + 1])
1395       add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1396       del document.body[i:j + 1]
1397       # \Abstract
1398       i = find_token(document.body, "\\begin_layout Abstract", 0)
1399       if i == -1:
1400         return
1401       j = find_end_of_layout(document.body, i)
1402       if j == -1:
1403         document.warning("Malformed LyX document: Can't find end of Abstract layout")
1404         i += 1
1405         continue
1406       content = lyx2latex(document, document.body[i:j + 1])
1407       add_to_preamble(document, ["\\Abstract{" + content + "}"])
1408       del document.body[i:j + 1]
1409       # \Keywords
1410       i = find_token(document.body, "\\begin_layout Keywords", 0)
1411       if i == -1:
1412         return
1413       j = find_end_of_layout(document.body, i)
1414       if j == -1:
1415         document.warning("Malformed LyX document: Can't find end of Keywords layout")
1416         i += 1
1417         continue
1418       content = lyx2latex(document, document.body[i:j + 1])
1419       add_to_preamble(document, ["\\Keywords{" + content + "}"])
1420       del document.body[i:j + 1]
1421       # \Plainkeywords
1422       i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1423       if i == -1:
1424         return
1425       j = find_end_of_layout(document.body, i)
1426       if j == -1:
1427         document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1428         i += 1
1429         continue
1430       content = lyx2latex(document, document.body[i:j + 1])
1431       add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1432       del document.body[i:j + 1]
1433       # \Address
1434       i = find_token(document.body, "\\begin_layout Address", 0)
1435       if i == -1:
1436         return
1437       j = find_end_of_layout(document.body, i)
1438       if j == -1:
1439         document.warning("Malformed LyX document: Can't find end of Address layout")
1440         i += 1
1441         continue
1442       content = lyx2latex(document, document.body[i:j + 1])
1443       add_to_preamble(document, ["\\Address{" + content + "}"])
1444       del document.body[i:j + 1]
1445       # finally handle the code layouts
1446       h = 0
1447       m = 0
1448       j = 0
1449       k = 0
1450       while m != -1 or j != -1 or h != -1 or k != -1:
1451         # \CodeChunk
1452         if h != -1:
1453           h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1454         if h != -1:
1455           endh = find_end_of_inset(document.body, h)
1456           document.body[endh + 1 : endh] = ["\\end_layout"]
1457           document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1458           document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1459           document.body[h - 1 : h] = ["\\begin_layout Standard"]
1460           h = h + 1
1461         # \CodeInput
1462         if j != -1:
1463           j = find_token(document.body, "\\begin_layout Code Input", j)
1464         if j != -1:
1465           endj = find_end_of_layout(document.body, j)
1466           document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1467           document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1468           document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1469           document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1470           document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1471           j = j + 1
1472         # \CodeOutput
1473         if k != -1:
1474           k = find_token(document.body, "\\begin_layout Code Output", k)
1475         if k != -1:
1476           endk = find_end_of_layout(document.body, k)
1477           document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1478           document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1479           document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1480           document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1481           document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1482           k = k + 1
1483         # \Code
1484         if m != -1:
1485           m = find_token(document.body, "\\begin_layout Code", m)
1486         if m != -1:
1487           endm = find_end_of_layout(document.body, m)
1488           document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1489           document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1490           document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1491           document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1492           document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1493           m = m + 1
1494
1495
1496 def convert_subref(document):
1497     " converts sub: ref prefixes to subref: "
1498
1499     # 1) label insets
1500     rx = re.compile(r'^name \"sub:(.+)$')
1501     i = 0
1502     while True:
1503         i = find_token(document.body, "\\begin_inset CommandInset label", i)
1504         if i == -1:
1505             break
1506         j = find_end_of_inset(document.body, i)
1507         if j == -1:
1508             document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1509             i += 1
1510             continue
1511
1512         for p in range(i, j):
1513             m = rx.match(document.body[p])
1514             if m:
1515                 label = m.group(1)
1516                 document.body[p] = "name \"subsec:" + label
1517         i += 1
1518
1519     # 2) xref insets
1520     rx = re.compile(r'^reference \"sub:(.+)$')
1521     i = 0
1522     while True:
1523         i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1524         if i == -1:
1525             return
1526         j = find_end_of_inset(document.body, i)
1527         if j == -1:
1528             document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1529             i += 1
1530             continue
1531
1532         for p in range(i, j):
1533             m = rx.match(document.body[p])
1534             if m:
1535                 label = m.group(1)
1536                 document.body[p] = "reference \"subsec:" + label
1537                 break
1538         i += 1
1539
1540
1541
1542 def revert_subref(document):
1543     " reverts subref: ref prefixes to sub: "
1544
1545     # 1) label insets
1546     rx = re.compile(r'^name \"subsec:(.+)$')
1547     i = 0
1548     while True:
1549         i = find_token(document.body, "\\begin_inset CommandInset label", i)
1550         if i == -1:
1551             break
1552         j = find_end_of_inset(document.body, i)
1553         if j == -1:
1554             document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1555             i += 1
1556             continue
1557
1558         for p in range(i, j):
1559             m = rx.match(document.body[p])
1560             if m:
1561                 label = m.group(1)
1562                 document.body[p] = "name \"sub:" + label
1563                 break
1564         i += 1
1565
1566     # 2) xref insets
1567     rx = re.compile(r'^reference \"subsec:(.+)$')
1568     i = 0
1569     while True:
1570         i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1571         if i == -1:
1572             return
1573         j = find_end_of_inset(document.body, i)
1574         if j == -1:
1575             document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1576             i += 1
1577             continue
1578
1579         for p in range(i, j):
1580             m = rx.match(document.body[p])
1581             if m:
1582                 label = m.group(1)
1583                 document.body[p] = "reference \"sub:" + label
1584                 break
1585         i += 1
1586
1587
1588 def convert_nounzip(document):
1589     " remove the noUnzip parameter of graphics insets "
1590
1591     rx = re.compile(r'\s*noUnzip\s*$')
1592     i = 0
1593     while True:
1594         i = find_token(document.body, "\\begin_inset Graphics", i)
1595         if i == -1:
1596             break
1597         j = find_end_of_inset(document.body, i)
1598         if j == -1:
1599             document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1600             i += 1
1601             continue
1602
1603         k = find_re(document.body, rx, i, j)
1604         if k != -1:
1605           del document.body[k]
1606           j = j - 1
1607         i = j + 1
1608
1609
1610 def convert_revert_external_bbox(document, forward):
1611     " add units to bounding box of external insets "
1612
1613     rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1614     i = 0
1615     while True:
1616         i = find_token(document.body, "\\begin_inset External", i)
1617         if i == -1:
1618             break
1619         j = find_end_of_inset(document.body, i)
1620         if j == -1:
1621             document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1622             i += 1
1623             continue
1624         k = find_re(document.body, rx, i, j)
1625         if k == -1:
1626             i = j + 1
1627             continue
1628         tokens = document.body[k].split()
1629         if forward:
1630             for t in range(1, 5):
1631                 tokens[t] += "bp"
1632         else:
1633             for t in range(1, 5):
1634                 tokens[t] = length_in_bp(tokens[t])
1635         document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1636                            tokens[3] + " " + tokens[4]
1637         i = j + 1
1638
1639
1640 def convert_external_bbox(document):
1641     convert_revert_external_bbox(document, True)
1642
1643
1644 def revert_external_bbox(document):
1645     convert_revert_external_bbox(document, False)
1646
1647
1648 def revert_tcolorbox_1(document):
1649   " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1650   i = -1
1651   while True:
1652     i = find_token(document.header, "tcolorbox", i)
1653     if i == -1:
1654       break
1655     else:    
1656       flex = 0
1657       flexEnd = -1
1658       flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1659       if flex == -1:
1660         return flexEnd
1661       flexEnd = find_end_of_inset(document.body, flex)
1662       wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1663       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1664       flexEnd = find_end_of_inset(document.body, flex)
1665       if wasOpt == True:
1666         document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1667       else:
1668         document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1669       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1670       flex += 1
1671
1672
1673 def revert_tcolorbox_2(document):
1674   " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1675   i = -1
1676   while True:
1677     i = find_token(document.header, "tcolorbox", i)
1678     if i == -1:
1679       break
1680     else:    
1681       flex = 0
1682       flexEnd = -1
1683       flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1684       if flex == -1:
1685         return flexEnd
1686       flexEnd = find_end_of_inset(document.body, flex)
1687       revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1688       flexEnd = find_end_of_inset(document.body, flex)
1689       document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1690       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1691       flex += 1
1692
1693
1694 def revert_tcolorbox_3(document):
1695   " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1696   i = -1
1697   while True:
1698     i = find_token(document.header, "tcolorbox", i)
1699     if i == -1:
1700       break
1701     else:    
1702       flex = 0
1703       flexEnd = -1
1704       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1705       if flex == -1:
1706         return flexEnd
1707       flexEnd = find_end_of_inset(document.body, flex)
1708       revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1709       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1710       flexEnd = find_end_of_inset(document.body, flex)
1711       document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1712       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1713       flex += 1
1714
1715
1716 def revert_tcolorbox_4(document):
1717   " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1718   i = -1
1719   while True:
1720     i = find_token(document.header, "tcolorbox", i)
1721     if i == -1:
1722       break
1723     else:    
1724       flex = 0
1725       flexEnd = -1
1726       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1727       if flex == -1:
1728         return flexEnd
1729       flexEnd = find_end_of_inset(document.body, flex)
1730       revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1731       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1732       flexEnd = find_end_of_inset(document.body, flex)
1733       document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1734       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1735       flex += 1
1736
1737
1738 def revert_tcolorbox_5(document):
1739   " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1740   i = -1
1741   while True:
1742     i = find_token(document.header, "tcolorbox", i)
1743     if i == -1:
1744       break
1745     else:    
1746       flex = 0
1747       flexEnd = -1
1748       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1749       if flex == -1:
1750         return flexEnd
1751       flexEnd = find_end_of_inset(document.body, flex)
1752       revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1753       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1754       flexEnd = find_end_of_inset(document.body, flex)
1755       document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1756       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1757       flex += 1
1758
1759
1760 def revert_tcolorbox_6(document):
1761   " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1762   i = -1
1763   while True:
1764     i = find_token(document.header, "tcolorbox", i)
1765     if i == -1:
1766       break
1767     else:    
1768       flex = 0
1769       flexEnd = -1
1770       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1771       if flex == -1:
1772         return flexEnd
1773       flexEnd = find_end_of_inset(document.body, flex)
1774       revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1775       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1776       flexEnd = find_end_of_inset(document.body, flex)
1777       document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1778       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1779       flex += 1
1780
1781
1782 def revert_tcolorbox_7(document):
1783   " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1784   i = -1
1785   while True:
1786     i = find_token(document.header, "tcolorbox", i)
1787     if i == -1:
1788       break
1789     else:    
1790       flex = 0
1791       flexEnd = -1
1792       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1793       if flex == -1:
1794         return flexEnd
1795       flexEnd = find_end_of_inset(document.body, flex)
1796       revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1797       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1798       flexEnd = find_end_of_inset(document.body, flex)
1799       document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1800       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1801       flex += 1
1802
1803
1804 def revert_tcolorbox_8(document):
1805   " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1806   i = 0
1807   j = 0
1808   k = 0
1809   while True:
1810     if i != -1:
1811       i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1812     if i != -1:
1813       j = find_end_of_layout(document.body, i)
1814       wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1815       revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1816       revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1817       document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1818       if wasOpt == True:
1819         document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1820       else:
1821         document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1822       k = find_end_of_inset(document.body, j)
1823       k = find_token(document.body, "\\end_inset", k + 1)
1824       k = find_token(document.body, "\\end_inset", k + 1)
1825       if wasOpt == True:
1826         k = find_token(document.body, "\\end_inset", k + 1)
1827       document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1828       j = find_token(document.body, "\\begin_layout Standard", j + 1)
1829       document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1830       i += 1
1831     if i == -1:
1832       return
1833
1834
1835 def revert_moderncv_1(document):
1836   " Reverts the new inset of moderncv to TeX-code in preamble "
1837   
1838   if document.textclass != "moderncv":
1839     return
1840   i = 0
1841   j = 0
1842   lineArg = 0
1843   while True:
1844     # at first revert the new styles
1845     # \moderncvicons
1846     i = find_token(document.body, "\\begin_layout CVIcons", 0)
1847     if i == -1:
1848       return
1849     j = find_end_of_layout(document.body, i)
1850     if j == -1:
1851       document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1852       i += 1
1853       continue
1854     content = lyx2latex(document, document.body[i:j + 1])
1855     add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1856     del document.body[i:j + 1]
1857     # \hintscolumnwidth
1858     i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1859     if i == -1:
1860       return
1861     j = find_end_of_layout(document.body, i)
1862     if j == -1:
1863       document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1864       i += 1
1865       continue
1866     content = lyx2latex(document, document.body[i:j + 1])
1867     add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1868     del document.body[i:j + 1]
1869     # now change the new styles to the obsolete ones
1870     # \name
1871     i = find_token(document.body, "\\begin_layout Name", 0)
1872     if i == -1:
1873       return
1874     j = find_end_of_layout(document.body, i)
1875     if j == -1:
1876       document.warning("Malformed LyX document: Can't find end of Name layout")
1877       i += 1
1878       continue
1879     lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1880     if lineArg > j and j != 0:
1881       return
1882     if lineArg != -1:
1883       beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1884       # we have to assure that no other inset is in the Argument
1885       beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1886       endInset = find_token(document.body, "\\end_inset", beginPlain)
1887       k = beginPlain + 1
1888       l = k
1889       while beginInset < endInset and beginInset != -1:
1890         beginInset = find_token(document.body, "\\begin_inset", k)
1891         endInset = find_token(document.body, "\\end_inset", l)
1892         k = beginInset + 1
1893         l = endInset + 1
1894       Arg2 = document.body[l + 5 : l + 6]
1895       # rename the style
1896       document.body[i : i + 1]= ["\\begin_layout FirstName"]
1897       # delete the Argument inset
1898       del( document.body[endInset - 2 : endInset + 3])
1899       del( document.body[lineArg : beginPlain + 1])
1900       document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1901
1902
1903 def revert_moderncv_2(document):
1904   " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1905   
1906   if document.textclass != "moderncv":
1907     return
1908   i = 0
1909   j = 0
1910   lineArg = 0
1911   while True:
1912     # \phone
1913     i = find_token(document.body, "\\begin_layout Phone", i)
1914     if i == -1:
1915       return
1916     j = find_end_of_layout(document.body, i)
1917     if j == -1:
1918       document.warning("Malformed LyX document: Can't find end of Phone layout")
1919       i += 1
1920       return
1921     lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1922     if lineArg > j and j != 0:
1923       i += 1
1924       continue
1925     if lineArg != -1:
1926       beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1927       # we have to assure that no other inset is in the Argument
1928       beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1929       endInset = find_token(document.body, "\\end_inset", beginPlain)
1930       k = beginPlain + 1
1931       l = k
1932       while beginInset < endInset and beginInset != -1:
1933         beginInset = find_token(document.body, "\\begin_inset", k)
1934         endInset = find_token(document.body, "\\end_inset", l)
1935         k = beginInset + 1
1936         l = endInset + 1
1937       Arg = document.body[beginPlain + 1 : beginPlain + 2]
1938       # rename the style
1939       if Arg[0] == "mobile":
1940         document.body[i : i + 1]= ["\\begin_layout Mobile"]
1941       if Arg[0] == "fax":
1942         document.body[i : i + 1]= ["\\begin_layout Fax"]
1943       # delete the Argument inset
1944       del(document.body[endInset - 2 : endInset + 1])
1945       del(document.body[lineArg : beginPlain + 3])
1946     i += 1
1947
1948
1949 def convert_moderncv(document):
1950   " Convert the Fax and Mobile inset of moderncv to the new phone inset "
1951   
1952   if document.textclass != "moderncv":
1953     return
1954   i = 0
1955   j = 0
1956   lineArg = 0
1957   while True:
1958     # \mobile
1959     i = find_token(document.body, "\\begin_layout Mobile", i)
1960     if i == -1:
1961       return
1962     j = find_end_of_layout(document.body, i)
1963     if j == -1:
1964       document.warning("Malformed LyX document: Can't find end of Mobile layout")
1965       i += 1
1966       return
1967     document.body[i + 1 : i + 1] = ["\\begin_inset Argument 1", "status open", "",
1968                         "\\begin_layout Plain Layout", "mobile", "\\end_layout", "",
1969                         "\\end_inset", ""]
1970     # \fax
1971     i = find_token(document.body, "\\begin_layout Fax", i)
1972     if i == -1:
1973       return
1974     j = find_end_of_layout(document.body, i)
1975     if j == -1:
1976       document.warning("Malformed LyX document: Can't find end of Fax layout")
1977       i += 1
1978       return
1979     document.body[i + 1 : i + 1] = ["\\begin_inset Argument 1", "status open", "",
1980                         "\\begin_layout Plain Layout", "fax", "\\end_layout", "",
1981                         "\\end_inset", ""]
1982     # \firstname and \familyname
1983     i1 = find_token(document.body, "\\begin_layout FirstName", 0)
1984     if i1 == -1:
1985       return
1986     j1 = find_end_of_layout(document.body, i1)
1987     if j1 == -1:
1988       document.warning("Malformed LyX document: Can't find end of FirstName layout")
1989       i1 += 1
1990       return
1991     FirstName = document.body[i1 + 1 : i1 + 2]
1992     i2 = find_token(document.body, "\\begin_layout FamilyName", 0)
1993     if i2 == -1:
1994       return
1995     j2 = find_end_of_layout(document.body, i2)
1996     if j2 == -1:
1997       document.warning("Malformed LyX document: Can't find end of FamilyName layout")
1998       i2 += 1
1999       return
2000     FamilyName = document.body[i2 + 1 : i2 + 2]
2001     if j1 > j2:
2002       k = j1
2003       l = i2
2004     else:
2005       k = j2
2006       l = i1
2007     document.body[k + 1 : k + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2008                         "\\begin_layout Plain Layout", FirstName[0], "\\end_layout", "",
2009                         "\\end_inset", "", FamilyName[0], "\\end_layout", ""]
2010     #document.body[i2 + 1 : i2 + 1] = ["hellok: ", str(k)]
2011     del(document.body[l : k])
2012     i += 1
2013     i1 += 1
2014     i2 += 1
2015
2016
2017 def revert_achemso(document):
2018   " Reverts the flex inset Latin to TeX code "
2019   
2020   if document.textclass != "achemso":
2021     return
2022   i = 0
2023   j = 0
2024   while True:
2025     i = find_token(document.body, "\\begin_inset Flex Latin", i)
2026     if i != -1:
2027       j = find_end_of_inset(document.body, i)
2028     else:
2029       return
2030     if j != -1:
2031       beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2032       endPlain = find_end_of_layout(document.body, beginPlain)
2033       content = lyx2latex(document, document.body[beginPlain : endPlain])
2034       document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2035     else:
2036       document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2037       return
2038     i += 1
2039
2040
2041 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2042                 "\\font_sf_scale", "\\font_tt_scale"]
2043 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2044 fontquotes = [True, True, True, True, False, False]
2045
2046 def convert_fontsettings(document):
2047     " Duplicate font settings "
2048
2049     i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2050     if i == -1:
2051         document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2052         use_non_tex_fonts = "false"
2053     else:
2054         use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2055     j = 0
2056     for f in fontsettings:
2057         i = find_token(document.header, f + " ", 0)
2058         if i == -1:
2059             document.warning("Malformed LyX document: No " + f + "!")
2060             j = j + 1
2061             continue
2062         value = document.header[i][len(f):].strip()
2063         if fontquotes[j]:
2064             if use_non_tex_fonts == "true":
2065                 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2066             else:
2067                 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2068         else:
2069             if use_non_tex_fonts == "true":
2070                 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2071             else:
2072                 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2073         j = j + 1
2074
2075
2076 def revert_fontsettings(document):
2077     " Merge font settings "
2078
2079     i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2080     if i == -1:
2081         document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2082         use_non_tex_fonts = "false"
2083     else:
2084         use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2085     j = 0
2086     for f in fontsettings:
2087         i = find_token(document.header, f + " ", 0)
2088         if i == -1:
2089             document.warning("Malformed LyX document: No " + f + "!")
2090             j = j + 1
2091             continue
2092         line = get_value(document.header, f, i)
2093         if fontquotes[j]:
2094             q1 = line.find('"')
2095             q2 = line.find('"', q1+1)
2096             q3 = line.find('"', q2+1)
2097             q4 = line.find('"', q3+1)
2098             if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2099                 document.warning("Malformed LyX document: Missing quotes!")
2100                 j = j + 1
2101                 continue
2102             if use_non_tex_fonts == "true":
2103                 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2104             else:
2105                 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2106         else:
2107             if use_non_tex_fonts == "true":
2108                 document.header[i:i+1] = [f + ' ' + line.split()[2]]
2109             else:
2110                 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2111         j = j + 1
2112
2113
2114 def revert_solution(document):
2115     " Reverts the solution environmen of the theorem module to TeX code "
2116
2117     # Do we use theorems-std module?
2118     have_mod = False
2119     mods = document.get_module_list()
2120     for mod in mods:
2121         if mod == "theorems-std" or mod == "theorems-bytype" \
2122         or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2123             have_mod = True
2124             continue
2125     if not have_mod:
2126         return
2127     consecutive = False
2128     i = 0
2129     while True:
2130         i = find_token(document.body, "\\begin_layout Solution", i)
2131         if i == -1:
2132             return
2133         j = find_end_of_layout(document.body, i)
2134         if j == -1:
2135             document.warning("Malformed LyX document: Can't find end of Solution layout")
2136             i += 1
2137             continue
2138         # if this is not a consecutive env, add start command
2139         begcmd = []
2140         if not consecutive:
2141             begcmd = put_cmd_in_ert("\\begin{sol}")
2142         # has this a consecutive theorem of same type?
2143         consecutive = False
2144         consecutive = document.body[j + 2] == "\\begin_layout Solution"
2145         # if this is not followed by a consecutive env, add end command
2146         if not consecutive:
2147             document.body[j : j + 1] = put_cmd_in_ert("\\end{sol}") + ["\\end_layout"]
2148         document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2149         add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2150         if mod == "theorems-std" or mod == "theorems-ams":
2151             add_to_preamble(document, "\\theoremstyle{plain}\n" \
2152                                       "\\newtheorem{sol}[thm]{\\protect\\solutionname}")
2153         if mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2154             add_to_preamble(document, "\\theoremstyle{definition}\n" \
2155                                       "\\newtheorem{sol}{\\protect\\solutionname}")
2156         i = j
2157
2158
2159 ##
2160 # Conversion hub
2161 #
2162
2163 supported_versions = ["2.2.0", "2.2"]
2164 convert = [
2165            [475, [convert_separator]],
2166            # nothing to do for 476: We consider it a bug that older versions
2167            # did not load amsmath automatically for these commands, and do not
2168            # want to hardcode amsmath off.
2169            [476, []],
2170            [477, []],
2171            [478, []],
2172            [479, []],
2173            [480, []],
2174            [481, [convert_dashes]],
2175            [482, [convert_phrases]],
2176            [483, [convert_specialchar]],
2177            [484, []],
2178            [485, []],
2179            [486, []],
2180            [487, []],
2181            [488, [convert_newgloss]],
2182            [489, [convert_BoxFeatures]],
2183            [490, [convert_origin]],
2184            [491, []],
2185            [492, [convert_colorbox]],
2186            [493, []],
2187            [494, []],
2188            [495, [convert_subref]],
2189            [496, [convert_nounzip]],
2190            [497, [convert_external_bbox]],
2191            [498, []],
2192            [499, [convert_moderncv]],
2193            [500, []],
2194            [501, [convert_fontsettings]],
2195            [502, []]
2196           ]
2197
2198 revert =  [
2199            [501, [revert_solution]],
2200            [500, [revert_fontsettings]],
2201            [499, [revert_achemso]],
2202            [498, [revert_moderncv_1, revert_moderncv_2]],
2203            [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2204                   revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2205                   revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2206            [496, [revert_external_bbox]],
2207            [495, []], # nothing to do since the noUnzip parameter was optional
2208            [494, [revert_subref]],
2209            [493, [revert_jss]],
2210            [492, [revert_mathmulticol]],
2211            [491, [revert_colorbox]],
2212            [490, [revert_textcolor]],
2213            [489, [revert_origin]],
2214            [488, [revert_BoxFeatures]],
2215            [487, [revert_newgloss, revert_glossgroup]],
2216            [486, [revert_forest]],
2217            [485, [revert_ex_itemargs]],
2218            [484, [revert_sigplan_doi]],
2219            [483, [revert_georgian]],
2220            [482, [revert_specialchar]],
2221            [481, [revert_phrases]],
2222            [480, [revert_dashes]],
2223            [479, [revert_question_env]],
2224            [478, [revert_beamer_lemma]],
2225            [477, [revert_xarrow]],
2226            [476, [revert_swissgerman]],
2227            [475, [revert_smash]],
2228            [474, [revert_separator]]
2229           ]
2230
2231
2232 if __name__ == "__main__":
2233     pass