def get_ert(lines, i): 'Convert an ERT inset into LaTeX.' if not lines[i].startswith("\\begin_inset ERT"): return "" j = find_end_of_inset(lines, i) if j == -1: return "" while i < j and not lines[i].startswith("status"): i = i + 1 i = i + 1 ret = "" first = True while i < j: if lines[i] == "\\begin_layout Plain Layout": if first: first = False else: ret = ret + "\n" while i + 1 < j and lines[i+1] == "": i = i + 1 elif lines[i] == "\\end_layout": while i + 1 < j and lines[i+1] == "": i = i + 1 elif lines[i] == "\\backslash": ret = ret + "\\" else: ret = ret + lines[i] i = i + 1 return ret
def revert_smash(document): " Set amsmath to on if smash commands are used " commands = ["smash[t]", "smash[b]", "notag"] i = find_token(document.header, "\\use_package amsmath", 0) if i == -1: document.warning("Malformed LyX document: Can't find \\use_package amsmath.") return; value = get_value(document.header, "\\use_package amsmath", i).split()[1] if value != "1": # nothing to do if package is not auto but on or off return; j = 0 while True: j = find_token(document.body, '\\begin_inset Formula', j) if j == -1: return k = find_end_of_inset(document.body, j) if k == -1: document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j)) j += 1 continue code = "\n".join(document.body[j:k]) for c in commands: if code.find("\\%s" % c) != -1: # set amsmath to on, since it is loaded by the newer format document.header[i] = "\\use_package amsmath 2" return j = k
def revert_use_package(document, pkg, commands, oldauto): # oldauto defines how the version we are reverting to behaves: # if it is true, the old version uses the package automatically. # if it is false, the old version never uses the package. regexp = re.compile(r'(\\use_package\s+%s)' % pkg) i = find_re(document.header, regexp, 0) value = "1" # default is auto if i != -1: value = get_value(document.header, "\\use_package" , i).split()[1] del document.header[i] if value == "2": # on add_to_preamble(document, ["\\usepackage{" + pkg + "}"]) elif value == "1" and not oldauto: # auto i = 0 while True: i = find_token(document.body, '\\begin_inset Formula', i) if i == -1: return j = find_end_of_inset(document.body, i) if j == -1: document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i)) i += 1 continue code = "\n".join(document.body[i:j]) for c in commands: if code.find("\\%s" % c) != -1: add_to_preamble(document, ["\\usepackage{" + pkg + "}"]) return i = j
def revert_flex_inset(lines, name, LaTeXname): " Convert flex insets to TeX code " i = 0 while True: i = find_token(lines, '\\begin_inset Flex ' + name, i) if i == -1: return z = find_end_of_inset(lines, i) if z == -1: document.warning("Can't find end of Flex " + name + " inset.") i += 1 continue # remove the \end_inset lines[z - 2:z + 1] = put_cmd_in_ert("}") # we need to reset character layouts if necessary j = find_token(lines, '\\emph on', i, z) k = find_token(lines, '\\noun on', i, z) l = find_token(lines, '\\series', i, z) m = find_token(lines, '\\family', i, z) n = find_token(lines, '\\shape', i, z) o = find_token(lines, '\\color', i, z) p = find_token(lines, '\\size', i, z) q = find_token(lines, '\\bar under', i, z) r = find_token(lines, '\\uuline on', i, z) s = find_token(lines, '\\uwave on', i, z) t = find_token(lines, '\\strikeout on', i, z) if j != -1: lines.insert(z - 2, "\\emph default") if k != -1: lines.insert(z - 2, "\\noun default") if l != -1: lines.insert(z - 2, "\\series default") if m != -1: lines.insert(z - 2, "\\family default") if n != -1: lines.insert(z - 2, "\\shape default") if o != -1: lines.insert(z - 2, "\\color inherit") if p != -1: lines.insert(z - 2, "\\size default") if q != -1: lines.insert(z - 2, "\\bar default") if r != -1: lines.insert(z - 2, "\\uuline default") if s != -1: lines.insert(z - 2, "\\uwave default") if t != -1: lines.insert(z - 2, "\\strikeout default") lines[i:i + 4] = put_cmd_in_ert(LaTeXname + "{") i += 1
def convert_dateinset(document): ' Convert date external inset to ERT ' i = 0 while True: i = find_token(document.body, "\\begin_inset External", i) if i == -1: return j = find_end_of_inset(document.body, i) if j == -1: document.warning( "Malformed lyx document: Missing '\\end_inset' in convert_dateinset." ) i += 1 continue if get_value(document.body, 'template', i, j) == "Date": document.body[i:j + 1] = put_cmd_in_ert("\\today ") i += 1 continue
def convert_lst_literalparam(document): " Add param literal to include inset " i = 0 while True: i = find_token(document.body, '\\begin_inset CommandInset include', i) if i == -1: break j = find_end_of_inset(document.body, i) if j == -1: document.warning( "Malformed LyX document: Can't find end of command inset at line %d" % i) i += 1 continue while i < j and document.body[i].strip() != '': i += 1 document.body.insert(i, "literal \"true\"")
def revert_lst_literalparam(document): " Remove param literal from include inset " i = 0 while True: i = find_token(document.body, '\\begin_inset CommandInset include', i) if i == -1: break j = find_end_of_inset(document.body, i) if j == -1: document.warning( "Malformed LyX document: Can't find end of include inset at line %d" % i) i += 1 continue k = find_token(document.body, 'literal', i, j) if k == -1: i += 1 continue del document.body[k]
def lyx2latex(document, lines): 'Convert some LyX stuff into corresponding LaTeX stuff, as best we can.' content = "" ert_end = 0 note_end = 0 hspace = "" for curline in range(len(lines)): line = lines[curline] if line.startswith("\\begin_inset Note Note"): # We want to skip LyX notes, so remember where the inset ends note_end = find_end_of_inset(lines, curline + 1) continue elif note_end >= curline: # Skip LyX notes continue elif line.startswith("\\begin_inset ERT"): # We don't want to replace things inside ERT, so figure out # where the end of the inset is. ert_end = find_end_of_inset(lines, curline + 1) continue elif line.startswith("\\begin_inset Formula"): line = line[20:] elif line.startswith("\\begin_inset Quotes"): # For now, we do a very basic reversion. Someone who understands # quotes is welcome to fix it up. qtype = line[20:].strip() # lang = qtype[0] side = qtype[1] dbls = qtype[2] if side == "l": if dbls == "d": line = "``" else: line = "`" else: if dbls == "d": line = "''" else: line = "'" elif line.startswith("\\begin_inset Newline newline"): line = "\\\\ " elif line.startswith("\\noindent"): line = "\\noindent " # we need the space behind the command elif line.startswith("\\begin_inset space"): line = line[18:].strip() if line.startswith("\\hspace"): # Account for both \hspace and \hspace* hspace = line[:-2] continue elif line == "\\space{}": line = "\\ " elif line == "\\thinspace{}": line = "\\," elif hspace != "": # The LyX length is in line[8:], after the \length keyword length = latex_length(line[8:])[1] line = hspace + "{" + length + "}" hspace = "" elif line.isspace() or \ line.startswith("\\begin_layout") or \ line.startswith("\\end_layout") or \ line.startswith("\\begin_inset") or \ line.startswith("\\end_inset") or \ line.startswith("\\lang") or \ line.strip() == "status collapsed" or \ line.strip() == "status open": #skip all that stuff continue # this needs to be added to the preamble because of cases like # \textmu, \textbackslash, etc. add_to_preamble(document, ['% added by lyx2lyx for converted index entries', '\\@ifundefined{textmu}', ' {\\usepackage{textcomp}}{}']) # a lossless reversion is not possible # try at least to handle some common insets and settings if ert_end >= curline: line = line.replace(r'\backslash', '\\') else: # No need to add "{}" after single-nonletter macros line = line.replace('&', '\\&') line = line.replace('#', '\\#') line = line.replace('^', '\\textasciicircum{}') line = line.replace('%', '\\%') line = line.replace('_', '\\_') line = line.replace('$', '\\$') # Do the LyX text --> LaTeX conversion for rep in unicode_reps: line = line.replace(rep[1], rep[0] + "{}") line = line.replace(r'\backslash', r'\textbackslash{}') line = line.replace(r'\series bold', r'\bfseries{}').replace(r'\series default', r'\mdseries{}') line = line.replace(r'\shape italic', r'\itshape{}').replace(r'\shape smallcaps', r'\scshape{}') line = line.replace(r'\shape slanted', r'\slshape{}').replace(r'\shape default', r'\upshape{}') line = line.replace(r'\emph on', r'\em{}').replace(r'\emph default', r'\em{}') line = line.replace(r'\noun on', r'\scshape{}').replace(r'\noun default', r'\upshape{}') line = line.replace(r'\bar under', r'\underbar{').replace(r'\bar default', r'}') line = line.replace(r'\family sans', r'\sffamily{}').replace(r'\family default', r'\normalfont{}') line = line.replace(r'\family typewriter', r'\ttfamily{}').replace(r'\family roman', r'\rmfamily{}') line = line.replace(r'\InsetSpace ', r'').replace(r'\SpecialChar ', r'') content += line return content
def lyx2latex(document, lines): 'Convert some LyX stuff into corresponding LaTeX stuff, as best we can.' content = "" ert_end = 0 note_end = 0 hspace = "" for curline in range(len(lines)): line = lines[curline] if line.startswith("\\begin_inset Note Note"): # We want to skip LyX notes, so remember where the inset ends note_end = find_end_of_inset(lines, curline + 1) continue elif note_end >= curline: # Skip LyX notes continue elif line.startswith("\\begin_inset ERT"): # We don't want to replace things inside ERT, so figure out # where the end of the inset is. ert_end = find_end_of_inset(lines, curline + 1) continue elif line.startswith("\\begin_inset Formula"): line = line[20:] elif line.startswith("\\begin_inset Quotes"): # For now, we do a very basic reversion. Someone who understands # quotes is welcome to fix it up. qtype = line[20:].strip() # lang = qtype[0] side = qtype[1] dbls = qtype[2] if side == "l": if dbls == "d": line = "``" else: line = "`" else: if dbls == "d": line = "''" else: line = "'" elif line.startswith("\\begin_inset Newline newline"): line = "\\\\ " elif line.startswith("\\noindent"): line = "\\noindent " # we need the space behind the command elif line.startswith("\\begin_inset space"): line = line[18:].strip() if line.startswith("\\hspace"): # Account for both \hspace and \hspace* hspace = line[:-2] continue elif line == "\\space{}": line = "\\ " elif line == "\\thinspace{}": line = "\\," elif hspace != "": # The LyX length is in line[8:], after the \length keyword length = latex_length(line[8:])[1] line = hspace + "{" + length + "}" hspace = "" elif line.isspace() or \ line.startswith("\\begin_layout") or \ line.startswith("\\end_layout") or \ line.startswith("\\begin_inset") or \ line.startswith("\\end_inset") or \ line.startswith("\\lang") or \ line.strip() == "status collapsed" or \ line.strip() == "status open": #skip all that stuff continue # this needs to be added to the preamble because of cases like # \textmu, \textbackslash, etc. add_to_preamble(document, [ '% added by lyx2lyx for converted index entries', '\\@ifundefined{textmu}', ' {\\usepackage{textcomp}}{}' ]) # a lossless reversion is not possible # try at least to handle some common insets and settings if ert_end >= curline: line = line.replace(r'\backslash', '\\') else: # No need to add "{}" after single-nonletter macros line = line.replace('&', '\\&') line = line.replace('#', '\\#') line = line.replace('^', '\\textasciicircum{}') line = line.replace('%', '\\%') line = line.replace('_', '\\_') line = line.replace('$', '\\$') # Do the LyX text --> LaTeX conversion for rep in unicode_reps: line = line.replace(rep[1], rep[0]) line = line.replace(r'\backslash', r'\textbackslash{}') line = line.replace(r'\series bold', r'\bfseries{}').replace( r'\series default', r'\mdseries{}') line = line.replace(r'\shape italic', r'\itshape{}').replace(r'\shape smallcaps', r'\scshape{}') line = line.replace(r'\shape slanted', r'\slshape{}').replace(r'\shape default', r'\upshape{}') line = line.replace(r'\emph on', r'\em{}').replace(r'\emph default', r'\em{}') line = line.replace(r'\noun on', r'\scshape{}').replace(r'\noun default', r'\upshape{}') line = line.replace(r'\bar under', r'\underbar{').replace(r'\bar default', r'}') line = line.replace(r'\family sans', r'\sffamily{}').replace( r'\family default', r'\normalfont{}') line = line.replace(r'\family typewriter', r'\ttfamily{}').replace( r'\family roman', r'\rmfamily{}') line = line.replace(r'\InsetSpace ', r'').replace(r'\SpecialChar ', r'') content += line return content
def revert_separator(document): " Revert separator insets to layout separators " beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"] if document.textclass in beamer_classes: beglaysep = "\\begin_layout Separator" else: beglaysep = "\\begin_layout --Separator--" parsep = [beglaysep, "", "\\end_layout", ""] comert = ["\\begin_inset ERT", "status collapsed", "", "\\begin_layout Plain Layout", "%", "\\end_layout", "", "\\end_inset", ""] empert = ["\\begin_inset ERT", "status collapsed", "", "\\begin_layout Plain Layout", " ", "\\end_layout", "", "\\end_inset", ""] i = 0 while 1: i = find_token(document.body, "\\begin_inset Separator", i) if i == -1: return lay = get_containing_layout(document.body, i) if lay == False: document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i)) i = i + 1 continue layoutname = lay[0] beg = lay[1] end = lay[2] kind = get_value(document.body, "\\begin_inset Separator", i, i+1, "plain").split()[1] before = document.body[beg+1:i] something_before = len(before) > 0 and len("".join(before)) > 0 j = find_end_of_inset(document.body, i) after = document.body[j+1:end] something_after = len(after) > 0 and len("".join(after)) > 0 if kind == "plain": beg = beg + len(before) + 1 elif something_before: document.body[i:i] = ["\\end_layout", ""] i = i + 2 j = j + 2 beg = i end = end + 2 if kind == "plain": if something_after: document.body[beg:j+1] = empert i = i + len(empert) else: document.body[beg:j+1] = comert i = i + len(comert) else: if something_after: if layoutname == "Standard": if not something_before: document.body[beg:j+1] = parsep i = i + len(parsep) document.body[i:i] = ["", "\\begin_layout Standard"] i = i + 2 else: document.body[beg:j+1] = ["\\begin_layout Standard"] i = i + 1 else: document.body[beg:j+1] = ["\\begin_deeper"] i = i + 1 end = end + 1 - (j + 1 - beg) if not something_before: document.body[i:i] = parsep i = i + len(parsep) end = end + len(parsep) document.body[i:i] = ["\\begin_layout Standard"] document.body[end+2:end+2] = ["", "\\end_deeper", ""] i = i + 4 else: next_par_is_aligned = False k = find_nonempty_line(document.body, end+1) if k != -1 and check_token(document.body[k], "\\begin_layout"): lay = get_containing_layout(document.body, k) next_par_is_aligned = lay != False and \ find_token(document.body, "\\align", lay[1], lay[2]) != -1 if k != -1 and not next_par_is_aligned \ and not check_token(document.body[k], "\\end_deeper") \ and not check_token(document.body[k], "\\begin_deeper"): if layoutname == "Standard": document.body[beg:j+1] = [beglaysep] i = i + 1 else: document.body[beg:j+1] = ["\\begin_deeper", beglaysep] end = end + 2 - (j + 1 - beg) document.body[end+1:end+1] = ["", "\\end_deeper", ""] i = i + 3 else: if something_before: del document.body[i:end+1] else: del document.body[i:end-1] i = i + 1
def revert_beamer_lemma(document): " Reverts beamer lemma layout to ERT " beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"] if document.textclass not in beamer_classes: return consecutive = False i = 0 while True: i = find_token(document.body, "\\begin_layout Lemma", i) if i == -1: return j = find_end_of_layout(document.body, i) if j == -1: document.warning( "Malformed LyX document: Can't find end of Lemma layout") i += 1 continue arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j) endarg1 = find_end_of_inset(document.body, arg1) arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j) endarg2 = find_end_of_inset(document.body, arg2) subst1 = [] subst2 = [] if arg1 != -1: beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1) if beginPlain1 == -1: document.warning( "Malformed LyX document: Can't find arg1 plain Layout") i += 1 continue endPlain1 = find_end_of_inset(document.body, beginPlain1) content1 = document.body[beginPlain1 + 1:endPlain1 - 2] subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">") if arg2 != -1: beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2) if beginPlain2 == -1: document.warning( "Malformed LyX document: Can't find arg2 plain Layout") i += 1 continue endPlain2 = find_end_of_inset(document.body, beginPlain2) content2 = document.body[beginPlain2 + 1:endPlain2 - 2] subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]") # remove Arg insets if arg1 < arg2: del document.body[arg2:endarg2 + 1] if arg1 != -1: del document.body[arg1:endarg1 + 1] if arg2 < arg1: del document.body[arg1:endarg1 + 1] if arg2 != -1: del document.body[arg2:endarg2 + 1] # index of end layout has probably changed j = find_end_of_layout(document.body, i) if j == -1: document.warning( "Malformed LyX document: Can't find end of Lemma layout") i += 1 continue begcmd = [] # if this is not a consecutive env, add start command if not consecutive: begcmd = put_cmd_in_ert("\\begin{lemma}") # has this a consecutive lemma? consecutive = document.body[j + 2] == "\\begin_layout Lemma" # if this is not followed by a consecutive env, add end command if not consecutive: document.body[j:j + 1] = put_cmd_in_ert("\\end{lemma}") + [ "\\end_layout" ] document.body[i:i + 1] = ["\\begin_layout Standard", "" ] + begcmd + subst1 + subst2 i = j
def revert_separator(document): " Revert separator insets to layout separators " beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"] if document.textclass in beamer_classes: beglaysep = "\\begin_layout Separator" else: beglaysep = "\\begin_layout --Separator--" parsep = [beglaysep, "", "\\end_layout", ""] comert = [ "\\begin_inset ERT", "status collapsed", "", "\\begin_layout Plain Layout", "%", "\\end_layout", "", "\\end_inset", "" ] empert = [ "\\begin_inset ERT", "status collapsed", "", "\\begin_layout Plain Layout", " ", "\\end_layout", "", "\\end_inset", "" ] i = 0 while 1: i = find_token(document.body, "\\begin_inset Separator", i) if i == -1: return lay = get_containing_layout(document.body, i) if lay == False: document.warning( "Malformed LyX document: Can't convert separator inset at line " + str(i)) i = i + 1 continue layoutname = lay[0] beg = lay[1] end = lay[2] kind = get_value(document.body, "\\begin_inset Separator", i, i + 1, "plain").split()[1] before = document.body[beg + 1:i] something_before = len(before) > 0 and len("".join(before)) > 0 j = find_end_of_inset(document.body, i) after = document.body[j + 1:end] something_after = len(after) > 0 and len("".join(after)) > 0 if kind == "plain": beg = beg + len(before) + 1 elif something_before: document.body[i:i] = ["\\end_layout", ""] i = i + 2 j = j + 2 beg = i end = end + 2 if kind == "plain": if something_after: document.body[beg:j + 1] = empert i = i + len(empert) else: document.body[beg:j + 1] = comert i = i + len(comert) else: if something_after: if layoutname == "Standard": if not something_before: document.body[beg:j + 1] = parsep i = i + len(parsep) document.body[i:i] = ["", "\\begin_layout Standard"] i = i + 2 else: document.body[beg:j + 1] = ["\\begin_layout Standard"] i = i + 1 else: document.body[beg:j + 1] = ["\\begin_deeper"] i = i + 1 end = end + 1 - (j + 1 - beg) if not something_before: document.body[i:i] = parsep i = i + len(parsep) end = end + len(parsep) document.body[i:i] = ["\\begin_layout Standard"] document.body[end + 2:end + 2] = ["", "\\end_deeper", ""] i = i + 4 else: next_par_is_aligned = False k = find_nonempty_line(document.body, end + 1) if k != -1 and check_token(document.body[k], "\\begin_layout"): lay = get_containing_layout(document.body, k) next_par_is_aligned = lay != False and \ find_token(document.body, "\\align", lay[1], lay[2]) != -1 if k != -1 and not next_par_is_aligned \ and not check_token(document.body[k], "\\end_deeper") \ and not check_token(document.body[k], "\\begin_deeper"): if layoutname == "Standard": document.body[beg:j + 1] = [beglaysep] i = i + 1 else: document.body[beg:j + 1] = ["\\begin_deeper", beglaysep] end = end + 2 - (j + 1 - beg) document.body[end + 1:end + 1] = ["", "\\end_deeper", ""] i = i + 3 else: if something_before: del document.body[i:end + 1] else: del document.body[i:end - 1] i = i + 1
def revert_beamer_lemma(document): " Reverts beamer lemma layout to ERT " beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"] if document.textclass not in beamer_classes: return consecutive = False i = 0 while True: i = find_token(document.body, "\\begin_layout Lemma", i) if i == -1: return j = find_end_of_layout(document.body, i) if j == -1: document.warning("Malformed LyX document: Can't find end of Lemma layout") i += 1 continue arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j) endarg1 = find_end_of_inset(document.body, arg1) arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j) endarg2 = find_end_of_inset(document.body, arg2) subst1 = [] subst2 = [] if arg1 != -1: beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1) if beginPlain1 == -1: document.warning("Malformed LyX document: Can't find arg1 plain Layout") i += 1 continue endPlain1 = find_end_of_inset(document.body, beginPlain1) content1 = document.body[beginPlain1 + 1 : endPlain1 - 2] subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">") if arg2 != -1: beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2) if beginPlain2 == -1: document.warning("Malformed LyX document: Can't find arg2 plain Layout") i += 1 continue endPlain2 = find_end_of_inset(document.body, beginPlain2) content2 = document.body[beginPlain2 + 1 : endPlain2 - 2] subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]") # remove Arg insets if arg1 < arg2: del document.body[arg2 : endarg2 + 1] if arg1 != -1: del document.body[arg1 : endarg1 + 1] if arg2 < arg1: del document.body[arg1 : endarg1 + 1] if arg2 != -1: del document.body[arg2 : endarg2 + 1] # index of end layout has probably changed j = find_end_of_layout(document.body, i) if j == -1: document.warning("Malformed LyX document: Can't find end of Lemma layout") i += 1 continue begcmd = [] # if this is not a consecutive env, add start command if not consecutive: begcmd = put_cmd_in_ert("\\begin{lemma}") # has this a consecutive lemma? consecutive = document.body[j + 2] == "\\begin_layout Lemma" # if this is not followed by a consecutive env, add end command if not consecutive: document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"] document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2 i = j