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