def change_insetgraphics(document): " Change inset Graphics." lines = document.body i = 0 while True: i = find_token(lines, "\\begin_inset Graphics", i) if i == -1: break j = find_end_of_inset(lines, i) lines[i] = "\\begin_inset Graphics" if get_value(lines, "display", i, j) == "default": j = del_token(lines, "display", i, j) if get_value(lines, "rotateOrigin", i, j) == "leftBaseline": j = del_token(lines, "rotateOrigin", i, j) k = find_token_exact(lines, "rotate", i, j) if k != -1: del lines[k] j = j - 1 else: j = del_token(lines, "rotateAngle", i, j) k = find_token_exact(lines, "size_type", i, j) if k == -1: k = find_token_exact(lines, "size_kind", i, j) if k != -1: size_type = lines[k].split()[1] del lines[k] j = j - 1 if size_type in ["0", "original"]: j = del_token(lines, "width", i, j) j = del_token(lines, "height", i, j) j = del_token(lines, "scale", i, j) elif size_type in ["2", "scale"]: j = del_token(lines, "width", i, j) j = del_token(lines, "height", i, j) if get_value(lines, "scale", i, j) == "100": j = del_token(lines, "scale", i, j) else: j = del_token(lines, "scale", i, j) k = find_token_exact(lines, "lyxsize_type", i, j) if k == -1: k = find_token_exact(lines, "lyxsize_kind", i, j) if k != -1: lyxsize_type = lines[k].split()[1] del lines[k] j = j - 1 j = del_token(lines, "lyxwidth", i, j) j = del_token(lines, "lyxheight", i, j) if lyxsize_type not in ["2", "scale"] or \ get_value(lines, "lyxscale", i, j) == "100": j = del_token(lines, "lyxscale", i, j) i = i + 1
def change_insetgraphics(document): " Change inset Graphics." lines = document.body i = 0 while 1: i = find_token(lines, "\\begin_inset Graphics", i) if i == -1: break j = find_end_of_inset(lines, i) lines[i] = "\\begin_inset Graphics" if get_value(lines, "display", i, j) == "default": j = del_token(lines, "display", i, j) if get_value(lines, "rotateOrigin", i, j) == "leftBaseline": j = del_token(lines, "rotateOrigin", i, j) k = find_token_exact(lines, "rotate", i, j) if k != -1: del lines[k] j = j-1 else: j = del_token(lines, "rotateAngle", i, j) k = find_token_exact(lines, "size_type", i, j) if k == -1: k = find_token_exact(lines, "size_kind", i, j) if k != -1: size_type = lines[k].split()[1] del lines[k] j = j-1 if size_type in ["0", "original"]: j = del_token(lines, "width", i, j) j = del_token(lines, "height", i, j) j = del_token(lines, "scale", i, j) elif size_type in ["2", "scale"]: j = del_token(lines, "width", i, j) j = del_token(lines, "height", i, j) if get_value(lines, "scale", i, j) == "100": j = del_token(lines, "scale", i, j) else: j = del_token(lines, "scale", i, j) k = find_token_exact(lines, "lyxsize_type", i, j) if k == -1: k = find_token_exact(lines, "lyxsize_kind", i, j) if k != -1: lyxsize_type = lines[k].split()[1] del lines[k] j = j-1 j = del_token(lines, "lyxwidth", i, j) j = del_token(lines, "lyxheight", i, j) if lyxsize_type not in ["2", "scale"] or \ get_value(lines, "lyxscale", i, j) == "100": j = del_token(lines, "lyxscale", i, j) i = i+1
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_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_paratype(document): " Revert ParaType font definitions to LaTeX " if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: preamble = "" i1 = find_token(document.header, "\\font_roman \"PTSerif-TLF\"", 0) i2 = find_token(document.header, "\\font_sans \"default\"", 0) i3 = find_token(document.header, "\\font_typewriter \"default\"", 0) j = find_token(document.header, "\\font_sans \"PTSans-TLF\"", 0) sfval = get_value(document.header, "\\font_sf_scale", 0) # cutoff " 100" sfval = sfval[:-4] sfoption = "" if sfval != "100": sfoption = "scaled=" + format(float(sfval) / 100, '.2f') k = find_token(document.header, "\\font_typewriter \"PTMono-TLF\"", 0) ttval = get_value(document.header, "\\font_tt_scale", 0) # cutoff " 100" ttval = ttval[:-4] ttoption = "" if ttval != "100": ttoption = "scaled=" + format(float(ttval) / 100, '.2f') if i1 != -1 and i2 != -1 and i3 != -1: add_to_preamble(document, ["\\usepackage{paratype}"]) else: if i1 != -1: add_to_preamble(document, ["\\usepackage{PTSerif}"]) document.header[i1] = document.header[i1].replace( "PTSerif-TLF", "default") if j != -1: if sfoption != "": add_to_preamble(document, ["\\usepackage[" + sfoption + "]{PTSans}"]) else: add_to_preamble(document, ["\\usepackage{PTSans}"]) document.header[j] = document.header[j].replace( "PTSans-TLF", "default") if k != -1: if ttoption != "": add_to_preamble(document, ["\\usepackage[" + ttoption + "]{PTMono}"]) else: add_to_preamble(document, ["\\usepackage{PTMono}"]) document.header[k] = document.header[k].replace( "PTMono-TLF", "default")
def remove_document_option(document, option): """ Remove _option_ as a document option.""" i = find_token(document.header, "\\options") options = get_value(document.header, "\\options", i) options = [op.strip() for op in options.split(',')] # Remove `option` from \options options = [op for op in options if op != option] if options: document.header[i] = "\\options " + ','.join(options) else: del document.header[i]
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 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 read(self): """Reads a file into the self.header and self.body parts, from self.input.""" # First pass: Read header to determine file encoding # If we are running under python3 then all strings are binary in this # pass. In some cases we need to convert binary to unicode in order to # use our parser tools. Since we do not know the true encoding yet we # use latin1. This works since a) the parts we are interested in are # pure ASCII (subset of latin1) and b) in contrast to pure ascii or # utf8, one can decode any 8byte string using latin1. first_line = True while True: line = self.input.readline() if not line: # eof found before end of header self.error("Invalid LyX file: Missing body.") if first_line: # Remove UTF8 BOM marker if present if line.startswith(codecs.BOM_UTF8): line = line[len(codecs.BOM_UTF8):] first_line = False if PY2: line = trim_eol(line) decoded = line else: line = trim_eol_binary(line) decoded = line.decode('latin1') if check_token(decoded, '\\begin_preamble'): while True: line = self.input.readline() if not line: # eof found before end of header self.error("Invalid LyX file: Missing body.") if PY2: line = trim_eol(line) decoded = line else: line = trim_eol_binary(line) decoded = line.decode('latin1') if check_token(decoded, '\\end_preamble'): break if decoded.split()[:0] in ("\\layout", "\\begin_layout", "\\begin_body"): self.warning("Malformed LyX file:" "Missing '\\end_preamble'." "\nAdding it now and hoping" "for the best.") self.preamble.append(line) if check_token(decoded, '\\end_preamble'): continue line = line.rstrip() if not line: continue if decoded.split()[0] in ("\\layout", "\\begin_layout", "\\begin_body", "\\begin_deeper"): self.body.append(line) break self.header.append(line) if PY2: i = find_token(self.header, '\\textclass', 0) else: i = find_token(self.header, b'\\textclass', 0) if i == -1: self.warning("Malformed LyX file: Missing '\\textclass'.") if PY2: i = find_token(self.header, '\\lyxformat', 0) + 1 self.header[i:i] = ['\\textclass article'] else: i = find_token(self.header, b'\\lyxformat', 0) + 1 self.header[i:i] = [b'\\textclass article'] if PY2: self.textclass = get_value(self.header, "\\textclass", 0, default = "") self.language = get_value(self.header, "\\language", 0, default = "english") self.inputencoding = get_value(self.header, "\\inputencoding", 0, default = "auto") else: self.textclass = get_value(self.header, b"\\textclass", 0, default = b"") self.language = get_value(self.header, b"\\language", 0, default = b"english").decode('ascii') self.inputencoding = get_value(self.header, b"\\inputencoding", 0, default = b"auto").decode('ascii') self.format = self.read_format() self.initial_format = self.format self.encoding = get_encoding(self.language, self.inputencoding, self.format, self.cjk_encoding) self.initial_version = self.read_version() # Second pass over header and preamble, now we know the file encoding # Do not forget the textclass (Debian bug #700828) self.textclass = self.textclass.decode(self.encoding) self.backend = get_backend(self.textclass) for i in range(len(self.header)): self.header[i] = self.header[i].decode(self.encoding) for i in range(len(self.preamble)): self.preamble[i] = self.preamble[i].decode(self.encoding) for i in range(len(self.body)): self.body[i] = self.body[i].decode(self.encoding) # Read document body while True: line = self.input.readline().decode(self.encoding) if not line: break self.body.append(trim_eol(line))
def read(self): """Reads a file into the self.header and self.body parts, from self.input.""" # First pass: Read header to determine file encoding # If we are running under python3 then all strings are binary in this # pass. In some cases we need to convert binary to unicode in order to # use our parser tools. Since we do not know the true encoding yet we # use latin1. This works since a) the parts we are interested in are # pure ASCII (subset of latin1) and b) in contrast to pure ascii or # utf8, one can decode any 8byte string using latin1. first_line = True while True: line = self.input.readline() if not line: # eof found before end of header self.error("Invalid LyX file: Missing body.") if first_line: # Remove UTF8 BOM marker if present if line.startswith(codecs.BOM_UTF8): line = line[len(codecs.BOM_UTF8):] first_line = False if PY2: line = trim_eol(line) decoded = line else: line = trim_eol_binary(line) decoded = line.decode('latin1') if check_token(decoded, '\\begin_preamble'): while True: line = self.input.readline() if not line: # eof found before end of header self.error("Invalid LyX file: Missing body.") if PY2: line = trim_eol(line) decoded = line else: line = trim_eol_binary(line) decoded = line.decode('latin1') if check_token(decoded, '\\end_preamble'): break if decoded.split()[:0] in ("\\layout", "\\begin_layout", "\\begin_body"): self.warning("Malformed LyX file:" "Missing '\\end_preamble'." "\nAdding it now and hoping" "for the best.") self.preamble.append(line) if check_token(decoded, '\\end_preamble'): continue line = line.rstrip() if not line: continue if decoded.split()[0] in ("\\layout", "\\begin_layout", "\\begin_body", "\\begin_deeper"): self.body.append(line) break self.header.append(line) if PY2: i = find_token(self.header, '\\textclass', 0) else: i = find_token(self.header, b'\\textclass', 0) if i == -1: self.warning("Malformed LyX file: Missing '\\textclass'.") if PY2: i = find_token(self.header, '\\lyxformat', 0) + 1 self.header[i:i] = ['\\textclass article'] else: i = find_token(self.header, b'\\lyxformat', 0) + 1 self.header[i:i] = [b'\\textclass article'] if PY2: self.textclass = get_value(self.header, "\\textclass", 0, default="") self.language = get_value(self.header, "\\language", 0, default="english") self.inputencoding = get_value(self.header, "\\inputencoding", 0, default="auto") else: self.textclass = get_value(self.header, b"\\textclass", 0, default=b"") self.language = get_value(self.header, b"\\language", 0, default=b"english").decode('ascii') self.inputencoding = get_value(self.header, b"\\inputencoding", 0, default=b"auto").decode('ascii') self.format = self.read_format() self.initial_format = self.format self.encoding = get_encoding(self.language, self.inputencoding, self.format, self.cjk_encoding) self.initial_version = self.read_version() # Second pass over header and preamble, now we know the file encoding # Do not forget the textclass (Debian bug #700828) self.textclass = self.textclass.decode(self.encoding) self.backend = get_backend(self.textclass) for i in range(len(self.header)): self.header[i] = self.header[i].decode(self.encoding) for i in range(len(self.preamble)): self.preamble[i] = self.preamble[i].decode(self.encoding) for i in range(len(self.body)): self.body[i] = self.body[i].decode(self.encoding) # Read document body while True: line = self.input.readline().decode(self.encoding) if not line: break self.body.append(trim_eol(line))
def remove_figinset(document): " Remove figinset." lines = document.body i = 0 while 1: i = find_token(lines, "\\begin_inset Figure", i) if i == -1: break j = find_end_of_inset(lines, i) if (len(lines[i].split()) > 2): lyxwidth = lines[i].split()[3] + "pt" lyxheight = lines[i].split()[4] + "pt" else: lyxwidth = "" lyxheight = "" filename = get_value(lines, "file", i + 1, j) width = get_length(lines, "width", i + 1, j) # what does width=5 mean ? height = get_length(lines, "height", i + 1, j) rotateAngle = get_value(lines, "angle", i + 1, j) if width == "" and height == "": size_type = "0" else: size_type = "1" flags = get_value(lines, "flags", i + 1, j) x = int(flags) % 4 if x == 1: display = "monochrome" elif x == 2: display = "gray" else: display = "color" subcaptionText = "" subcaptionLine = find_token(lines, "subcaption", i + 1, j) if subcaptionLine != -1: subcaptionText = lines[subcaptionLine][11:] if subcaptionText != "": subcaptionText = '"' + subcaptionText + '"' k = find_token(lines, "subfigure", i + 1, j) if k == -1: subcaption = 0 else: subcaption = 1 new = ["\\begin_inset Graphics FormatVersion 1"] write_attribute(new, "filename", filename) write_attribute(new, "display", display) if subcaption: new.append("\tsubcaption") write_attribute(new, "subcaptionText", subcaptionText) write_attribute(new, "size_type", size_type) write_attribute(new, "width", width) write_attribute(new, "height", height) if rotateAngle != "": new.append("\trotate") write_attribute(new, "rotateAngle", rotateAngle) write_attribute(new, "rotateOrigin", "leftBaseline") write_attribute(new, "lyxsize_type", "1") write_attribute(new, "lyxwidth", lyxwidth) write_attribute(new, "lyxheight", lyxheight) new = new + ["\\end_inset"] lines[i:j + 1] = new
def revert_language(document, lyxname, babelname="", polyglossianame=""): " Revert native language support " # Does the document use polyglossia? use_polyglossia = False if get_bool_value(document.header, "\\use_non_tex_fonts"): i = find_token(document.header, "\\language_package") if i == -1: document.warning("Malformed document! Missing \\language_package") else: pack = get_value(document.header, "\\language_package", i) if pack == "default" or pack == "auto": use_polyglossia = True # Do we use this language with polyglossia? with_polyglossia = use_polyglossia and polyglossianame != "" # Do we use this language with babel? with_babel = with_polyglossia == False and babelname != "" # Are we dealing with a primary or secondary language? primary = document.language == lyxname secondary = False # Main language first orig_doc_language = document.language if primary: # Change LyX document language to English (we will tell LaTeX # to use the original language at the end of this function): document.language = "english" i = find_token(document.header, "\\language %s" % lyxname, 0) if i != -1: document.header[i] = "\\language english" # Now look for occurences in the body i = 0 while True: i = find_token(document.body, "\\lang", i+1) if i == -1: break if document.body[i].startswith("\\lang %s" % lyxname): secondary = True texname = use_polyglossia and polyglossianame or babelname elif primary and document.body[i].startswith("\\lang english"): # Since we switched the main language manually, English parts need to be marked texname = "english" else: continue parent = get_containing_layout(document.body, i) i_e = parent[2] # end line no, # print(i, texname, parent, document.body[i+1], file=sys.stderr) # Move leading space to the previous line: if document.body[i+1].startswith(" "): document.body[i+1] = document.body[i+1][1:] document.body.insert(i, " ") continue # Ensure correct handling of list labels if (parent[0] in ["Labeling", "Description"] and not " " in "\n".join(document.body[parent[3]:i])): # line `i+1` is first line of a list item, # part before a space character is the label # TODO: insets or language change before first space character labelline = document.body[i+1].split(' ', 1) if len(labelline) > 1: # Insert a space in the (original) document language # between label and remainder. # print(" Label:", labelline, file=sys.stderr) lines = [labelline[0], "\\lang %s" % orig_doc_language, " ", "\\lang %s" % (primary and "english" or lyxname), labelline[1]] document.body[i+1:i+2] = lines i_e += 4 # Find out where to end the language change. langswitch = i while True: langswitch = find_token(document.body, "\\lang", langswitch+1, i_e) if langswitch == -1: break # print(" ", langswitch, document.body[langswitch], file=sys.stderr) # skip insets i_a = parent[3] # paragraph start line container = get_containing_inset(document.body[i_a:i_e], langswitch-i_a) if container and container[1] < langswitch-i_a and container[2] > langswitch-i_a: # print(" inset", container, file=sys.stderr) continue i_e = langswitch break # use function or environment? singlepar = i_e - i < 3 if not singlepar and parent[0] == "Plain Layout": # environment not allowed in some insets container = get_containing_inset(document.body, i) singlepar = container[0] in singlepar_insets # Delete empty language switches: if not "".join(document.body[i+1:i_e]): del document.body[i:i_e] i -= 1 continue if singlepar: if with_polyglossia: begin_cmd = "\\text%s{"%texname elif with_babel: begin_cmd = "\\foreignlanguage{%s}{" % texname end_cmd = "}" else: if with_polyglossia: begin_cmd = "\\begin{%s}"%texname end_cmd = "\\end{%s}"%texname elif with_babel: begin_cmd = "\\begin{otherlanguage}{%s}" % texname end_cmd = "\\end{otherlanguage}" if (not primary or texname == "english"): document.body[i_e:i_e] = put_cmd_in_ert(end_cmd) document.body[i+1:i+1] = put_cmd_in_ert(begin_cmd) del document.body[i] if not (primary or secondary): return # Make the language known to Babel/Polyglossia and ensure the correct # document language: doc_lang_switch = "" if with_babel: # add as global option insert_document_option(document, babelname) # Since user options are appended to the document options, # Babel will treat `babelname` as primary language. if not primary: doc_lang_switch = "\\selectlanguage{%s}" % orig_doc_language if with_polyglossia: # Define language in the user preamble # (don't use \AtBeginDocument, this fails with some languages). add_to_preamble(document, ["\\usepackage{polyglossia}", "\\setotherlanguage{%s}" % polyglossianame]) if primary: # Changing the main language must be done in the document body. doc_lang_switch = "\\resetdefaultlanguage{%s}" % polyglossianame # Reset LaTeX main language if required and not already done if doc_lang_switch and doc_lang_switch not in document.body[8:20] != doc_lang_switch: document.body[2:2] = put_cmd_in_ert(doc_lang_switch, is_open=True, as_paragraph=True)
def revert_language(document, lyxname, babelname="", polyglossianame=""): " Revert native language support " # Does the document use polyglossia? use_polyglossia = False if get_bool_value(document.header, "\\use_non_tex_fonts"): i = find_token(document.header, "\\language_package") if i == -1: document.warning("Malformed document! Missing \\language_package") else: pack = get_value(document.header, "\\language_package", i) if pack in ("default", "auto"): use_polyglossia = True # Do we use this language with polyglossia? with_polyglossia = use_polyglossia and polyglossianame != "" # Do we use this language with babel? with_babel = with_polyglossia == False and babelname != "" # Are we dealing with a primary or secondary language? primary = document.language == lyxname secondary = False # Main language first orig_doc_language = document.language if primary: # Change LyX document language to English (we will tell LaTeX # to use the original language at the end of this function): document.language = "english" i = find_token(document.header, "\\language %s" % lyxname, 0) if i != -1: document.header[i] = "\\language english" # Now look for occurences in the body i = 0 while True: i = find_token(document.body, "\\lang", i + 1) if i == -1: break if document.body[i].startswith("\\lang %s" % lyxname): secondary = True texname = use_polyglossia and polyglossianame or babelname elif primary and document.body[i].startswith("\\lang english"): # Since we switched the main language manually, English parts need to be marked texname = "english" else: continue parent = get_containing_layout(document.body, i) i_e = parent[2] # end line no, # print(i, texname, parent, document.body[i+1], file=sys.stderr) # Move leading space to the previous line: if document.body[i + 1].startswith(" "): document.body[i + 1] = document.body[i + 1][1:] document.body.insert(i, " ") continue # TODO: handle nesting issues with font attributes, e.g. # \begin_layout Standard # # \emph on # \lang macedonian # Македонски јазик # \emph default # — јужнословенски јазик, дел од групата на словенски јазици од јазичното # семејство на индоевропски јазици. # Македонскиот е службен и национален јазик во Македонија. # \end_layout # Ensure correct handling of list labels if (parent[0] in ["Labeling", "Description"] and not " " in "\n".join(document.body[parent[3]:i])): # line `i+1` is first line of a list item, # part before a space character is the label # TODO: insets or language change before first space character labelline = document.body[i + 1].split(' ', 1) if len(labelline) > 1: # Insert a space in the (original) document language # between label and remainder. # print(" Label:", labelline, file=sys.stderr) lines = [ labelline[0], "\\lang %s" % orig_doc_language, " ", "\\lang %s" % (primary and "english" or lyxname), labelline[1] ] document.body[i + 1:i + 2] = lines i_e += 4 # Find out where to end the language change. langswitch = i while True: langswitch = find_token(document.body, "\\lang", langswitch + 1, i_e) if langswitch == -1: break # print(" ", langswitch, document.body[langswitch], file=sys.stderr) # skip insets i_a = parent[3] # paragraph start line container = get_containing_inset(document.body[i_a:i_e], langswitch - i_a) if container and container[1] < langswitch - i_a and container[ 2] > langswitch - i_a: # print(" inset", container, file=sys.stderr) continue i_e = langswitch break # use function or environment? singlepar = i_e - i < 3 if not singlepar and parent[0] == "Plain Layout": # environment not allowed in some insets container = get_containing_inset(document.body, i) singlepar = container[0] in singlepar_insets # Delete empty language switches: if not "".join(document.body[i + 1:i_e]): del document.body[i:i_e] i -= 1 continue if singlepar: if with_polyglossia: begin_cmd = "\\text%s{" % texname elif with_babel: begin_cmd = "\\foreignlanguage{%s}{" % texname end_cmd = "}" else: if with_polyglossia: begin_cmd = "\\begin{%s}" % texname end_cmd = "\\end{%s}" % texname elif with_babel: begin_cmd = "\\begin{otherlanguage}{%s}" % texname end_cmd = "\\end{otherlanguage}" if (not primary or texname == "english"): try: document.body[i_e:i_e] = put_cmd_in_ert(end_cmd) document.body[i + 1:i + 1] = put_cmd_in_ert(begin_cmd) except UnboundLocalError: pass del document.body[i] if not (primary or secondary): return # Make the language known to Babel/Polyglossia and ensure the correct # document language: doc_lang_switch = "" if with_babel: # add as global option insert_document_option(document, babelname) # Since user options are appended to the document options, # Babel will treat `babelname` as primary language. if not primary: doc_lang_switch = "\\selectlanguage{%s}" % orig_doc_language if with_polyglossia: # Define language in the user preamble # (don't use \AtBeginDocument, this fails with some languages). add_to_preamble(document, [ "\\usepackage{polyglossia}", "\\setotherlanguage{%s}" % polyglossianame ]) if primary: # Changing the main language must be done in the document body. doc_lang_switch = "\\resetdefaultlanguage{%s}" % polyglossianame # Reset LaTeX main language if required and not already done if doc_lang_switch and doc_lang_switch[1:] not in document.body[8:20]: document.body[2:2] = put_cmd_in_ert(doc_lang_switch, is_open=True, as_paragraph=True)
def is_document_option(document, option): "Find if _option_ is a document option" options = get_value(document.header, "\\options") options = [op.strip() for op in options.split(',')] return option in options
def read(self): """Reads a file into the self.header and self.body parts, from self.input.""" while True: line = self.input.readline() if not line: self.error("Invalid LyX file.") line = trim_eol(line) if check_token(line, '\\begin_preamble'): while 1: line = self.input.readline() if not line: self.error("Invalid LyX file.") line = trim_eol(line) if check_token(line, '\\end_preamble'): break if line.split()[:0] in ("\\layout", "\\begin_layout", "\\begin_body"): self.warning("Malformed LyX file:" "Missing '\\end_preamble'." "\nAdding it now and hoping" "for the best.") self.preamble.append(line) if check_token(line, '\\end_preamble'): continue line = line.strip() if not line: continue if line.split()[0] in ("\\layout", "\\begin_layout", "\\begin_body", "\\begin_deeper"): self.body.append(line) break self.header.append(line) i = find_token(self.header, '\\textclass', 0) if i == -1: self.warning("Malformed LyX file: Missing '\\textclass'.") i = find_token(self.header, '\\lyxformat', 0) + 1 self.header[i:i] = ['\\textclass article'] self.textclass = get_value(self.header, "\\textclass", 0) self.backend = get_backend(self.textclass) self.format = self.read_format() self.language = get_value(self.header, "\\language", 0, default = "english") self.inputencoding = get_value(self.header, "\\inputencoding", 0, default = "auto") self.encoding = get_encoding(self.language, self.inputencoding, self.format, self.cjk_encoding) self.initial_version = self.read_version() # Second pass over header and preamble, now we know the file encoding # Do not forget the textclass (Debian bug #700828) self.textclass = self.textclass.decode(self.encoding) for i in range(len(self.header)): self.header[i] = self.header[i].decode(self.encoding) for i in range(len(self.preamble)): self.preamble[i] = self.preamble[i].decode(self.encoding) # Read document body while 1: line = self.input.readline().decode(self.encoding) if not line: break self.body.append(trim_eol(line))
def remove_figinset(document): " Remove figinset." lines = document.body i = 0 while 1: i = find_token(lines, "\\begin_inset Figure", i) if i == -1: break j = find_end_of_inset(lines, i) if ( len(lines[i].split()) > 2 ): lyxwidth = lines[i].split()[3]+"pt" lyxheight = lines[i].split()[4]+"pt" else: lyxwidth = "" lyxheight = "" filename = get_value(lines, "file", i+1, j) width = get_length(lines, "width", i+1, j) # what does width=5 mean ? height = get_length(lines, "height", i+1, j) rotateAngle = get_value(lines, "angle", i+1, j) if width == "" and height == "": size_type = "0" else: size_type = "1" flags = get_value(lines, "flags", i+1, j) x = int(flags)%4 if x == 1: display = "monochrome" elif x == 2: display = "gray" else: display = "color" subcaptionText = "" subcaptionLine = find_token(lines, "subcaption", i+1, j) if subcaptionLine != -1: subcaptionText = lines[subcaptionLine][11:] if subcaptionText != "": subcaptionText = '"'+subcaptionText+'"' k = find_token(lines, "subfigure", i+1,j) if k == -1: subcaption = 0 else: subcaption = 1 new = ["\\begin_inset Graphics FormatVersion 1"] write_attribute(new, "filename", filename) write_attribute(new, "display", display) if subcaption: new.append("\tsubcaption") write_attribute(new, "subcaptionText", subcaptionText) write_attribute(new, "size_type", size_type) write_attribute(new, "width", width) write_attribute(new, "height", height) if rotateAngle != "": new.append("\trotate") write_attribute(new, "rotateAngle", rotateAngle) write_attribute(new, "rotateOrigin", "leftBaseline") write_attribute(new, "lyxsize_type", "1") write_attribute(new, "lyxwidth", lyxwidth) write_attribute(new, "lyxheight", lyxheight) new = new + ["\\end_inset"] lines[i:j+1] = new
def read(self): """Reads a file into the self.header and self.body parts, from self.input.""" while True: line = self.input.readline() if not line: self.error("Invalid LyX file.") line = trim_eol(line) if check_token(line, '\\begin_preamble'): while 1: line = self.input.readline() if not line: self.error("Invalid LyX file.") line = trim_eol(line) if check_token(line, '\\end_preamble'): break if line.split()[:0] in ("\\layout", "\\begin_layout", "\\begin_body"): self.warning("Malformed LyX file:" "Missing '\\end_preamble'." "\nAdding it now and hoping" "for the best.") self.preamble.append(line) if check_token(line, '\\end_preamble'): continue line = line.strip() if not line: continue if line.split()[0] in ("\\layout", "\\begin_layout", "\\begin_body", "\\begin_deeper"): self.body.append(line) break self.header.append(line) i = find_token(self.header, '\\textclass', 0) if i == -1: self.warning("Malformed LyX file: Missing '\\textclass'.") i = find_token(self.header, '\\lyxformat', 0) + 1 self.header[i:i] = ['\\textclass article'] self.textclass = get_value(self.header, "\\textclass", 0) self.backend = get_backend(self.textclass) self.format = self.read_format() self.language = get_value(self.header, "\\language", 0, default="english") self.inputencoding = get_value(self.header, "\\inputencoding", 0, default="auto") self.encoding = get_encoding(self.language, self.inputencoding, self.format, self.cjk_encoding) self.initial_version = self.read_version() # Second pass over header and preamble, now we know the file encoding # Do not forget the textclass (Debian bug #700828) self.textclass = self.textclass.decode(self.encoding) for i in range(len(self.header)): self.header[i] = self.header[i].decode(self.encoding) for i in range(len(self.preamble)): self.preamble[i] = self.preamble[i].decode(self.encoding) # Read document body while 1: line = self.input.readline().decode(self.encoding) if not line: break self.body.append(trim_eol(line))
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