def append_local_layout(self, new_layout): " Append `new_layout` to the local layouts." # new_layout may be a string or a list of strings (lines) try: new_layout = new_layout.splitlines() except AttributeError: pass i = find_token(self.header, "\\begin_local_layout", 0) if i == -1: k = find_token(self.header, "\\language", 0) if k == -1: # this should not happen self.warning( "Malformed LyX document! No \\language header found!") return self.header[k:k] = ["\\begin_local_layout", "\\end_local_layout"] i = k j = find_end_of(self.header, i, "\\begin_local_layout", "\\end_local_layout") if j == -1: # this should not happen self.warning( "Malformed LyX document: Can't find end of local layout!") return self.header[i + 1:i + 1] = new_layout
def convert_beamer_article_styles(document): " Remove included (scr)article styles in beamer article " beamer_articles = ["article-beamer", "scrarticle-beamer"] if document.textclass not in beamer_articles: return while True: i = find_token(document.header, "\\begin_local_layout", 0) if i == -1: return j = find_end_of(document.header, i, "\\begin_local_layout", "\\end_local_layout") if j == -1: # this should not happen break k = find_token( document.header, "### Inserted by lyx2lyx (more [scr]article styles) ###", i, j) if k != -1: l = find_token( document.header, "### End of insertion by lyx2lyx (more [scr]article styles) ###", i, j) if l == -1: # this should not happen document.warning( "End of lyx2lyx local layout insertion not found!") break document.header[k:l + 1] = [] return
def get_toc(self, depth = 4): " Returns the TOC of this LyX document." paragraphs_filter = {'Title' : 0,'Chapter' : 1, 'Section' : 2, 'Subsection' : 3, 'Subsubsection': 4} allowed_insets = ['Quotes'] allowed_parameters = ('\\paragraph_spacing', '\\noindent', '\\align', '\\labelwidthstring', "\\start_of_appendix", "\\leftindent") sections = [] for section in paragraphs_filter.keys(): sections.append('\\begin_layout %s' % section) toc_par = [] i = 0 while 1: i = find_tokens(self.body, sections, i) if i == -1: break j = find_end_of(self.body, i + 1, '\\begin_layout', '\\end_layout') if j == -1: self.warning('Incomplete file.', 0) break section = self.body[i].split()[1] if section[-1] == '*': section = section[:-1] par = [] k = i + 1 # skip paragraph parameters while not self.body[k].strip() or self.body[k].split()[0] \ in allowed_parameters: k += 1 while k < j: if check_token(self.body[k], '\\begin_inset'): inset = self.body[k].split()[1] end = find_end_of_inset(self.body, k) if end == -1 or end > j: self.warning('Malformed file.', 0) if inset in allowed_insets: par.extend(self.body[k: end+1]) k = end + 1 else: par.append(self.body[k]) k += 1 # trim empty lines in the end. while par and par[-1].strip() == '': par.pop() toc_par.append(Paragraph(section, par)) i = j + 1 return toc_par
def revert_beamer_article_styles(document): " Include (scr)article styles in beamer article " beamer_articles = ["article-beamer", "scrarticle-beamer"] if document.textclass not in beamer_articles: return inclusion = "article.layout" if document.textclass == "scrarticle-beamer": inclusion = "scrartcl.layout" while True: i = find_token(document.header, "\\begin_local_layout", 0) if i == -1: k = find_token(document.header, "\\language", 0) if k == -1: # this should not happen document.warning( "Malformed LyX document! No \\language header found!") break document.header[k - 1:k - 1] = [ "\\begin_local_layout", "\\end_local_layout" ] i = find_token(document.header, "\\begin_local_layout", 0) if i != -1: j = find_end_of(document.header, i, "\\begin_local_layout", "\\end_local_layout") if j == -1: # this should not happen break document.header[i + 1:i + 1] = [ "### Inserted by lyx2lyx (more [scr]article styles) ###", "Input " + inclusion, "Input beamer.layout", "Provides geometry 0", "Provides hyperref 0", "DefaultFont", " Family Roman", " Series Medium", " Shape Up", " Size Normal", " Color None", "EndFont", "Preamble", " \\usepackage{beamerarticle,pgf}", " % this default might be overridden by plain title style", " \\newcommand\makebeamertitle{\\frame{\\maketitle}}%", " \\AtBeginDocument{", " \\let\\origtableofcontents=\\tableofcontents", " \\def\\tableofcontents{\\@ifnextchar[{\\origtableofcontents}{\\gobbletableofcontents}}", " \\def\\gobbletableofcontents#1{\\origtableofcontents}", " }", "EndPreamble", "### End of insertion by lyx2lyx (more [scr]article styles) ###" ] return
def _lyxml2xml(self, start, end): lines = self.lines xout = self.xout i = start while i < end: self.dbg(3, 'Parsing line %d: %s' % (i, lines[i])) if lines[i].startswith('</'): xout.end_elt(lines[i][2:-2]) i += 1 elif lines[i].startswith('<'): (tag, attrs, start_tok, end_tok) = _parse_xml_tag(lines[i]) # There don't seem to be tags with no child nodes in .lyx, # but let's handle them just in case. if lines[i][-2] != '/': e = find_end_of(lines, i, start_tok, end_tok) self.dbg( 3, 'find_end_of(%d, %s, %s) = %d' % (i, start_tok, end_tok, e)) # lyxtabular's <column> and <features> don't get closed! # What a mess. if e == -1: e = None else: e = None xout.start_elt(tag) xout.attr('embedded_xml', 'true') for a in attrs: xout.attr(a[0], a[1]) if e: i = self._lyxml2xml(i + 1, e) self.dbg(3, '_lyx2xml(...) = %d, looking for %d' % (i, e)) if i + 1 == e: i += 1 else: self.dbg(3, '_lyx2xml() returned %d, e = %d' % (i, e)) else: xout.end_elt(tag) i += 1 else: i = self._lyx2xml(i, end) return i
def _lyxml2xml(self, start, end): lines = self.lines xout = self.xout i = start while i < end: self.dbg(3, "Parsing line {0:d}: {1!s}".format(i, lines[i])) if lines[i].startswith("</"): xout.end_elt(lines[i][2:-2]) i += 1 elif lines[i].startswith("<"): (tag, attrs, start_tok, end_tok) = _parse_xml_tag(lines[i]) # There don't seem to be tags with no child nodes in .lyx, # but let's handle them just in case. if lines[i][-2] != "/": e = find_end_of(lines, i, start_tok, end_tok) self.dbg(3, "find_end_of({0:d}, {1!s}, {2!s}) = {3:d}".format(i, start_tok, end_tok, e)) # lyxtabular's <column> and <features> don't get closed! # What a mess. if e == -1: e = None else: e = None xout.start_elt(tag) xout.attr("embedded_xml", "true") for a in attrs: xout.attr(a[0], a[1]) if e: i = self._lyxml2xml(i + 1, e) self.dbg(3, "_lyx2xml(...) = {0:d}, looking for {1:d}".format(i, e)) if i + 1 == e: i += 1 else: self.dbg(3, "_lyx2xml() returned {0:d}, e = {1:d}".format(i, e)) else: xout.end_elt(tag) i += 1 else: i = self._lyx2xml(i, end) return i
def _lyxml2xml(self, start, end): lines = self.lines xout = self.xout i = start while i < end: self.dbg(3, 'Parsing line %d: %s' % (i, lines[i])) if lines[i].startswith('</'): xout.end_elt(lines[i][2:-2]) i += 1 elif lines[i].startswith('<'): (tag, attrs, start_tok, end_tok) = _parse_xml_tag(lines[i]) # There don't seem to be tags with no child nodes in .lyx, # but let's handle them just in case. if lines[i][-2] != '/': e = find_end_of(lines, i, start_tok, end_tok) self.dbg(3, 'find_end_of(%d, %s, %s) = %d' % (i, start_tok, end_tok, e)) # lyxtabular's <column> and <features> don't get closed! # What a mess. if e == -1: e = None else: e = None xout.start_elt(tag) xout.attr('embedded_xml', 'true') for a in attrs: xout.attr(a[0], a[1]) if e: i = self._lyxml2xml(i + 1, e) self.dbg(3, '_lyx2xml(...) = %d, looking for %d' % (i, e)) if i + 1 == e: i += 1 else: self.dbg(3, '_lyx2xml() returned %d, e = %d' % (i, e)) else: xout.end_elt(tag) i += 1 else: i = self._lyx2xml(i, end) return i
def append_local_layout(self, new_layout): " Append `new_layout` to the local layouts." # new_layout may be a string or a list of strings (lines) try: new_layout = new_layout.splitlines() except AttributeError: pass i = find_token(self.header, "\\begin_local_layout", 0) if i == -1: k = find_token(self.header, "\\language", 0) if k == -1: # this should not happen self.warning("Malformed LyX document! No \\language header found!") return self.header[k-1 : k-1] = ["\\begin_local_layout", "\\end_local_layout"] i = k - 1 j = find_end_of(self.header, i, "\\begin_local_layout", "\\end_local_layout") if j == -1: # this should not happen self.warning("Malformed LyX document: Can't find end of local layout!") return self.header[i+1 : i+1] = new_layout
def _lyx2xml(self, start=0, end=-1, cmd_type=None): lines = self.lines xout = self.xout i = start if end < 0: end = len(lines) prev_i = -1 while i < end: self.dbg(3, 'Parsing line %d: %s' % (i, lines[i])) assert i > prev_i prev_i = i if len(lines[i]) == 0 or lines[i] == ' ': # Ignore empty lines i += 1 cmd_type = None elif lines[i][0] == '#': # LyX source comment #xout.comment(lines[i][1:]) i += 1 cmd_type = None elif _beginner(lines[i]): (el, start_tok, end_tok, cmd_type, rest) = _parse_begin(lines[i]) xout.start_elt(el) if el == 'document': # Default XML namespace xout.attr('xmlns', 'urn:cryptonector.com:lyx-other') # We put layouts, insets, and custome insets into # separate namespaces xout.attr('xmlns:layout', 'urn:cryptonector.com:lyx-layout') xout.attr('xmlns:inset', 'urn:cryptonector.com:lyx-inset') xout.attr('xmlns:flex', 'urn:cryptonector.com:lyx-flex') # XXX MathML not yet implemented; we get to turn LyX # formulas into MathML, joy! But it looks like # LaTeX math, and there's tools for converting that # to MathML, so, hey, it might not be too much work. xout.attr('xmlns:math', 'http://www.w3.org/1998/Math/MathML') # Suck in \lyxformat, which precedes \begin_document, # and anything else that might be there (though # nothing will be). self._handle_interspersed_attrs(0, i) if (el.startswith('inset:') or el.startswith('flex:')) and \ not cmd_type and (i + 1) < end and \ lines[i + 1].startswith('status '): i += 1 # skip status open|collapsed line status = _chomp(lines[i][lines[i].find(' ') + 1:]) else: status = None self.dbg(4, 'lines[%d] = %s' % (i, lines[i])) e = find_end_of(lines, i, start_tok, end_tok) assert e != -1 self.dbg( 4, 'find_end_of(%d, %s, %s) = %d' % (i, start_tok, end_tok, e)) # XXX Here we need to find any attributes that might be # interspersed with child nodes so we can suck them in # first. What a PITA. self._handle_interspersed_attrs(i + 1, e - 1) if status: xout.attr('status', status) if len(rest) == 2 and el != 'inset:Formula': xout.attr(rest[0], escape(rest[1])) i = self._lyx2xml(i + 1, e, cmd_type) self.dbg( 4, '_lyx2xml(...) = %d, looking for %s at %d; end = %d' % (i, end_tok, e, end)) if i + 1 == e: i += 1 else: self.dbg( 4, '_lyx2xml() returned %d, e = %d; end = %d' % (i, e, end)) assert lines[i].startswith('\\end_') if len(rest) == 2 and el == 'inset:Formula': xout.text(' ') xout.text(escape(rest[1])) xout.end_elt(el) cmd_type = None i += 1 elif cmd_type == 'inset': # Parse "\begin_inset CommandInset ..." attributes self.dbg(4, 'lines[%d] = %s' % (i, lines[i])) while i < end and lines[i] != '' and lines[i] != ' ': (a, v) = _parse_attr(lines[i]) if not a or not v: break xout.attr(a, v) i += 1 # then suck in content cmd_type = None elif cmd_type == 'XML': # Parse embedded XML contents i = self._lyxml2xml(i, end) cmd_type = None elif lines[i][0] == '\\' and \ not lines[i].startswith('\\begin_') and \ not lines[i].startswith('\\end_') and \ not _key(lines[i]) in mixed_tags: # An attribute, which we've handled above with the call to # _handle_interspersed_attrs(). i += 1 else: line = lines[i] if xout.stack[-1] == 'layout': line = _chomp(line) key = _key(line) if key in mixed_tags: val = _chomp(lines[i][lines[i].find(' ') + 1:]) if val == mixed_tags[key]: xout.end_elt(key) else: xout.start_elt(key) xout.attr('type', val) else: xout.text(escape(line)) cmd_type = None i += 1 return i
def find_end_of_inset(lines, i): " Find beginning of inset, where lines[i] is included." return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
def find_end_of_inset(lines, i): "Finds the matching \end_inset" return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
def find_end_of_tabular(lines, i): " Finds the matching end of tabular." return find_end_of(lines, i, "<lyxtabular", "</lyxtabular")
def find_end_of_inset(lines, i): " Finds the matching \end_inset" return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
def _lyx2xml(self, start=0, end=-1, cmd_type=None): lines = self.lines xout = self.xout i = start if end < 0: end = len(lines) prev_i = -1 while i < end: self.dbg(3, 'Parsing line %d: %s' % (i, lines[i])) assert i > prev_i prev_i = i if len(lines[i]) == 0 or lines[i] == ' ': # Ignore empty lines i += 1 cmd_type = None elif lines[i][0] == '#': # LyX source comment #xout.comment(lines[i][1:]) i += 1 cmd_type = None elif _beginner(lines[i]): (el, start_tok, end_tok, cmd_type, rest) = _parse_begin(lines[i]) xout.start_elt(el) if el == 'document': # Default XML namespace xout.attr('xmlns', 'urn:cryptonector.com:lyx-other') # We put layouts, insets, and custome insets into # separate namespaces xout.attr('xmlns:layout', 'urn:cryptonector.com:lyx-layout') xout.attr('xmlns:inset', 'urn:cryptonector.com:lyx-inset') xout.attr('xmlns:flex', 'urn:cryptonector.com:lyx-flex') # XXX MathML not yet implemented; we get to turn LyX # formulas into MathML, joy! But it looks like # LaTeX math, and there's tools for converting that # to MathML, so, hey, it might not be too much work. xout.attr('xmlns:math', 'http://www.w3.org/1998/Math/MathML') # Suck in \lyxformat, which precedes \begin_document, # and anything else that might be there (though # nothing will be). self._handle_interspersed_attrs(0, i) if (el.startswith('inset:') or el.startswith('flex:')) and \ not cmd_type and (i + 1) < end and \ lines[i + 1].startswith('status '): i += 1 # skip status open|collapsed line status = _chomp(lines[i][lines[i].find(' ') + 1:]) else: status = None self.dbg(4, 'lines[%d] = %s' % (i, lines[i])) e = find_end_of(lines, i, start_tok, end_tok) assert e != -1 self.dbg(4, 'find_end_of(%d, %s, %s) = %d' % (i, start_tok, end_tok, e)) # XXX Here we need to find any attributes that might be # interspersed with child nodes so we can suck them in # first. What a PITA. self._handle_interspersed_attrs(i + 1, e - 1) if status: xout.attr('status', status) if len(rest) == 2 and el != 'inset:Formula': xout.attr(rest[0], escape(rest[1])) i = self._lyx2xml(i + 1, e, cmd_type) self.dbg(4, '_lyx2xml(...) = %d, looking for %s at %d; end = %d' % (i, end_tok, e, end)) if i + 1 == e: i += 1 else: self.dbg(4, '_lyx2xml() returned %d, e = %d; end = %d' % (i, e, end)) assert lines[i].startswith('\\end_') if len(rest) == 2 and el == 'inset:Formula': xout.text(' ') xout.text(escape(rest[1])) xout.end_elt(el) cmd_type = None i += 1 elif cmd_type == 'inset': # Parse "\begin_inset CommandInset ..." attributes self.dbg(4, 'lines[%d] = %s' % (i, lines[i])) while i < end and lines[i] != '' and lines[i] != ' ': (a, v) = _parse_attr(lines[i]) if not a or not v: break xout.attr(a, v) i += 1 # then suck in content cmd_type = None elif cmd_type == 'XML': # Parse embedded XML contents i = self._lyxml2xml(i, end) cmd_type = None elif lines[i][0] == '\\' and \ not lines[i].startswith('\\begin_') and \ not lines[i].startswith('\\end_') and \ not _key(lines[i]) in mixed_tags: # An attribute, which we've handled above with the call to # _handle_interspersed_attrs(). i += 1 else: line = lines[i] if xout.stack[-1] == 'layout': line = _chomp(line) key = _key(line) if key in mixed_tags: val = _chomp(lines[i][lines[i].find(' ') + 1:]) if val == mixed_tags[key]: xout.end_elt(key) else: xout.start_elt(key) xout.attr('type', val) else: xout.text(escape(line)) cmd_type = None i += 1 return i
def _lyx2xml(self, start=0, end=-1, cmd_type=None): lines = self.lines xout = self.xout i = start if end < 0: end = len(lines) prev_i = -1 while i < end: self.dbg(3, "Parsing line {0:d}: {1!s}".format(i, lines[i])) assert i > prev_i prev_i = i if len(lines[i]) == 0 or lines[i] == " ": # Ignore empty lines i += 1 cmd_type = None elif lines[i][0] == "#": # LyX source comment # xout.comment(lines[i][1:]) i += 1 cmd_type = None elif _beginner(lines[i]): (el, start_tok, end_tok, cmd_type, rest) = _parse_begin(lines[i]) xout.start_elt(el) if el == "document": # Default XML namespace xout.attr("xmlns", "urn:cryptonector.com:lyx-other") # We put layouts, insets, and custome insets into # separate namespaces xout.attr("xmlns:layout", "urn:cryptonector.com:lyx-layout") xout.attr("xmlns:inset", "urn:cryptonector.com:lyx-inset") xout.attr("xmlns:flex", "urn:cryptonector.com:lyx-flex") # XXX MathML not yet implemented; we get to turn LyX # formulas into MathML, joy! But it looks like # LaTeX math, and there's tools for converting that # to MathML, so, hey, it might not be too much work. xout.attr("xmlns:math", "http://www.w3.org/1998/Math/MathML") # Suck in \lyxformat, which precedes \begin_document, # and anything else that might be there (though # nothing will be). self._handle_interspersed_attrs(0, i) if ( (el.startswith("inset:") or el.startswith("flex:")) and not cmd_type and (i + 1) < end and lines[i + 1].startswith("status ") ): i += 1 # skip status open|collapsed line status = _chomp(lines[i][lines[i].find(" ") + 1 :]) else: status = None self.dbg(4, "lines[{0:d}] = {1!s}".format(i, lines[i])) e = find_end_of(lines, i, start_tok, end_tok) assert e != -1 self.dbg(4, "find_end_of({0:d}, {1!s}, {2!s}) = {3:d}".format(i, start_tok, end_tok, e)) # XXX Here we need to find any attributes that might be # interspersed with child nodes so we can suck them in # first. What a PITA. self._handle_interspersed_attrs(i + 1, e - 1) if status: xout.attr("status", status) if len(rest) == 2 and el != "inset:Formula": xout.attr(rest[0], escape(rest[1])) i = self._lyx2xml(i + 1, e, cmd_type) self.dbg(4, "_lyx2xml(...) = {0:d}, looking for {1!s} at {2:d}; end = {3:d}".format(i, end_tok, e, end)) if i + 1 == e: i += 1 else: self.dbg(4, "_lyx2xml() returned {0:d}, e = {1:d}; end = {2:d}".format(i, e, end)) assert lines[i].startswith("\\end_") if len(rest) == 2 and el == "inset:Formula": xout.text(" ") xout.text(escape(rest[1])) xout.end_elt(el) cmd_type = None i += 1 elif cmd_type == "inset": # Parse "\begin_inset CommandInset ..." attributes self.dbg(4, "lines[{0:d}] = {1!s}".format(i, lines[i])) while i < end and lines[i] != "" and lines[i] != " ": (a, v) = _parse_attr(lines[i]) if not a or not v: break xout.attr(a, v) i += 1 # then suck in content cmd_type = None elif cmd_type == "XML": # Parse embedded XML contents i = self._lyxml2xml(i, end) cmd_type = None elif ( lines[i][0] == "\\" and not lines[i].startswith("\\begin_") and not lines[i].startswith("\\end_") and not _key(lines[i]) in mixed_tags ): # An attribute, which we've handled above with the call to # _handle_interspersed_attrs(). i += 1 else: line = lines[i] if xout.stack[-1] == "layout": line = _chomp(line) key = _key(line) if key in mixed_tags: val = _chomp(lines[i][lines[i].find(" ") + 1 :]) if val == mixed_tags[key]: xout.end_elt(key) else: xout.start_elt(key) xout.attr("type", val) else: xout.text(escape(line)) cmd_type = None i += 1 return i