The branch, odt2lyx, has been updated. - Log -----------------------------------------------------------------
commit c7ce5c8e36c3df6937cf80dc78563daa62e83a66 Author: Prannoy Pilligundla <[email protected]> Date: Mon Jul 21 17:35:30 2014 +0530 Changed the code structure and documented everything diff --git a/odt2lyx/parseodt.py b/odt2lyx/parseodt.py index 43e6036..73c587d 100644 --- a/odt2lyx/parseodt.py +++ b/odt2lyx/parseodt.py @@ -1,23 +1,35 @@ #!/usr/bin/env python -# import the needed modules +#Import the needed modules import zipfile import xml.parsers.expat import sys, codecs -# get content xml data from OpenDocument file -inputfile = sys.argv[1] -outputfile = inputfile[:-3] + "lyx" -ziparchive = zipfile.ZipFile(inputfile, "r") -xmldata = ziparchive.read("content.xml") +#Get content xml data from OpenDocument file +inputfile = sys.argv[1] #First argument is the input file which is supposed to be converted +outputfile = inputfile[:-3] + "lyx" #Output lyx file has the same name as the inputfile +ziparchive = zipfile.ZipFile(inputfile, "r") #Extracting the input ODT file +xmldata = ziparchive.read("content.xml") #Reading conten.xml of the extracted ODT file which has all the content ziparchive.close() + +#Variable which holds the entire content that needs to be written in the output lyx file +doc_content = "" + +docbody=False #True states that all the styles in content.xml have been processed and the body is being processed. Used in processElement() method of the Element class +list_deeper = False #True states that a multilevel list item has been encountered. Used in processList() method of the Element class -class Element(list): +class Element(list): + """Every tag in the xml file is an Element i.e object of this class. Only content present in the xml file(i.e content in between tags) doen't belong to this class""" + """Relationships: TreeBuilder. TreeBuilder class while making a tree using parser information calls this class and makes every tag an object of this class""" + """Responsibility: To process each Element and write appropriate lyx commands to the output lyx file""" + def __init__(self, name, attrs): + """Each xml tag has two properties, its name and attributes. So adding these properties to every Element object""" self.name = name self.attrs = attrs def isParagraph(self): + """To check whether that particular object is a LyX Paragraph Element or not""" node=self if len(node)>0: e = node[0] @@ -27,19 +39,21 @@ class Element(list): return False def processDescription(self): + """Called when description is encountered in the xml. Responsibility is to handle the description environment and write it appropriately into the lyx file""" global doc_content node=self for e in node: if isinstance(e, Element): if e.name == "text:list-item": doc_content = doc_content + "\n\\begin_layout Description\n" - showtree(e) + e.processElement() doc_content = doc_content + "\n\\end_layout\n" def processList(self): + """Called when a list is encountered in the xml. Responsibility is to handle Lists write it appropriately into the lyx file""" global doc_content, listStyles, list_deeper node=self - begin_deeper = False + begin_deeper = False #Becomes True if a multilevel list item has been encountered and True indicates that \begin_deeper has been written in the lyx file if list_deeper: begin_deeper = True doc_content = doc_content + "\n\\begin_deeper\n" @@ -48,13 +62,68 @@ class Element(list): if e[2].attrs.get(u'text:style-name',"") in {"Inside-itemize","Inside-enumerate"}: doc_content = doc_content + "\n\\begin_layout " + listStyles[node.attrs[u'text:style-name']] + "\n" list_deeper = True - showtree(e) + e.processElement() doc_content = doc_content + "\n\\end_layout\n" if begin_deeper: doc_content = doc_content + "\n\\end_deeper\n" list_deeper = False + + def processElement(self): + """Recursively called to get information about each node i.e each Element one by one""" + global doc_content, docbody, paragraphStyles, headingStyles + node = self + noSupport = False #True implies that a particular tag is of no use for the outputfile lyx file. + textStyle = False #True implies that a particular text style(other than default) is currently on + try: + if node.name=="office:body": + docbody=True + doc_content = doc_content + u'\n\\begin_body\n\n' + #At the point node.name and node.attrs will be processed and appropriate lyx command for it will be written into the lyx file + if docbody and node.name!="office:body" and node.name!="office:text": + #Checking if the node belongs to a Sectioning category of LyX + if node.attrs.get(u'text:style-name',"") in headingStyles.keys(): + doc_content = doc_content + "\n\\begin_layout " + headingStyles[node.attrs[u'text:style-name']] + "\n" + del node[0] + #Checking if the node belongs to a Paragraph style + elif node.attrs.get(u'text:style-name',"") in paragraphStyles.keys(): + if node.isParagraph(): + doc_content = doc_content + "\n\\begin_layout Paragraph\n" + node[0][0] + "\n\\end_layout\n" + del node[0] + if node.attrs[u'text:style-name']=="description": + node.processDescription() + return + doc_content = doc_content + "\n\\begin_layout " + paragraphStyles[node.attrs[u'text:style-name']] + "\n" + elif node.name=="text:span" and node.attrs.get(u'text:style-name',"") in textStyles.keys(): + doc_content = doc_content + "\n\\" + textStyles[node.attrs[u'text:style-name']][0] + " " + textStyles[node.attrs[u'text:style-name']][1] + "\n" + textStyle = True + #Handling lists here + elif node.name=="text:list" and node.attrs.get(u'text:style-name',"") in listStyles.keys(): + node.processList() + return + else: + noSupport = True + """Parsing through the node and handling each element inside node appropriately""" + for e in node: + if isinstance(e, Element): + """Now we are in a nested case for example, span inside paragraph tag. In a particular case node is paragraph and span is e, so we are calling + processElement() again to analyse span tag as the way paragraph was processed""" + e.processElement() + if node.name=="office:body": docbody=False + else: + #As "e" is not an instance of Element class here "e"contains content, e will be written as it is in the lyx document + if docbody and node.name!="office:body" and node.name!="office:text": + doc_content = doc_content + e + if docbody and node.name!="office:body" and node.name!="office:text" and not noSupport: + if textStyle: + doc_content = doc_content + "\n\\" + textStyles[node.attrs[u'text:style-name']][0] + " default\n" + else: + doc_content = doc_content + "\n\\end_layout\n" + except: + print "Error-",node.name, node.attrs class TreeBuilder: + """Responsibility: Build a tree using the expat xml parser""" + """Relationships: Element. Creates an Element object for every xml tag it encounters""" def __init__(self): self.root = Element("root", None) self.path = [self.root] @@ -68,22 +137,19 @@ class TreeBuilder: def char_data(self, data): self.path[-1].append(data) -# create parser and parsehandler +#Create parser and parsehandler parser = xml.parsers.expat.ParserCreate() -treebuilder = TreeBuilder() -# assign the handler functions -parser.StartElementHandler = treebuilder.start_element -parser.EndElementHandler = treebuilder.end_element -parser.CharacterDataHandler = treebuilder.char_data +ODTDocTree = TreeBuilder() +#Assign the handler functions to parser +parser.StartElementHandler = ODTDocTree.start_element #To handle start of a tag +parser.EndElementHandler = ODTDocTree.end_element #To Handle ending of a tag +parser.CharacterDataHandler = ODTDocTree.char_data #To Handle content in between starting and ending of a tag -# parse the data -xmldata = xmldata.replace("<text:s/>"," ") -xmldata = xmldata.replace("<text:line-break/>","") -parser.Parse(xmldata, True) #Now whole XML stream is parsed and the structure is stored in and accessible from treebuilder.root +#Parse the data +xmldata = xmldata.replace("<text:s/>"," ") #<text:s/> represnts space in xml terminology, so replacing it with a space +xmldata = xmldata.replace("<text:line-break/>","") # <text:line-break/> represnts line breaks, so removing it from the xml data +parser.Parse(xmldata, True) #Now whole XML stream is parsed and the structure is stored in and accessible from ODTDocTree.root -docbody=False -list_deeper = False -doc_content = "" #Header is not yet processed, this is temporary to be able to open the outpt file with LyX doc_header="""#LyX created this file. For more info see http://www.lyx.org/ \\lyxformat 474 @@ -128,59 +194,10 @@ listStyles = { 'Enumerate':'Enumerate' } - -def showtree(node): - """Recursively called to get information about each node one by one""" - global docbody,doc_content,paragraphStyles,headingStyles - noSupport = False - textStyle = False - try: - if node.name=="office:body": - docbody=True - doc_content = doc_content + u'\n\\begin_body\n\n' - #At the point node.name and node.attrs will be processed and appropriate lyx command for it will be written into the lyx file - if docbody and node.name!="office:body" and node.name!="office:text": - if node.attrs.get(u'text:style-name',"") in headingStyles.keys(): - doc_content = doc_content + "\n\\begin_layout " + headingStyles[node.attrs[u'text:style-name']] + "\n" - del node[0] - - elif node.attrs.get(u'text:style-name',"") in paragraphStyles.keys(): - if node.isParagraph(): - doc_content = doc_content + "\n\\begin_layout Paragraph\n" + node[0][0] + "\n\\end_layout\n" - del node[0] - if node.attrs[u'text:style-name']=="description": - node.processDescription() - return - doc_content = doc_content + "\n\\begin_layout " + paragraphStyles[node.attrs[u'text:style-name']] + "\n" - elif node.name=="text:span" and node.attrs.get(u'text:style-name',"") in textStyles.keys(): - doc_content = doc_content + "\n\\" + textStyles[node.attrs[u'text:style-name']][0] + " " + textStyles[node.attrs[u'text:style-name']][1] + "\n" - textStyle = True - #Handling lists here - elif node.name=="text:list" and node.attrs.get(u'text:style-name',"") in listStyles.keys(): - node.processList() - return - else: - noSupport = True - #doc_content = doc_content + "\n#start " + node.name + "\t" + node.attrs[u'text:style-name'] + "\n" - for e in node: - if isinstance(e, Element): - """Now we are in a nested case for example, span inside paragraph tag. In a particular case node is paragraph and span is e, so we are calling showtree() again - to analyse span tag as the way paragraph was processed""" - showtree(e) - if node.name=="office:body": docbody=False - else: - #As "e" is not an instance of Element class here "e"contains content, e will be written as it is in the lyx document - if docbody and node.name!="office:body" and node.name!="office:text": - doc_content = doc_content + e - if docbody and node.name!="office:body" and node.name!="office:text" and not noSupport: - if textStyle: - doc_content = doc_content + "\n\\" + textStyles[node.attrs[u'text:style-name']][0] + " default\n" - else: - doc_content = doc_content + "\n\\end_layout\n" - except: - print "Error-",node.name, node.attrs -showtree(treebuilder.root) +#ODTDocTree.root is of the type Element which essentially has all information about the entire content.xml of the ODT file +XMLTree = ODTDocTree.root +XMLTree.processElement() lyxoutput = codecs.open(outputfile, 'w', 'utf-8') doc_content = doc_header + doc_content + '\n\\end_body\n\\end_document\n' lyxoutput.write(doc_content) ----------------------------------------------------------------------- Summary of changes: odt2lyx/parseodt.py | 167 ++++++++++++++++++++++++++++----------------------- 1 files changed, 92 insertions(+), 75 deletions(-) hooks/post-receive -- Repositories for GSOC work
