!

Dette materialet blir ikke lenger vedlikeholdt. Du vil finne oppdatert materiale på siden: http://borres.hiof.no/wep/

Python Open Office
Børre Stenseth
Python >Open Office

Open Office og Python

Hva
Inspeksjon av Open Office dokument

I denne modulen skal vi pakke ut og inspisere filer laget av Open Office Writer. Utgangspunktet er at en .odt fil egentlig er en .zip fil som vi kan pakke ut. Du kan forsøke dette ved å

  1. Lage en fil, doc.odt, i Open Office Writer. Du kan legge inn bilder, overskrifter osv.
  2. Lagre fila på disken, lage en kopi og kalle kopien doc.zip.
  3. Åpne zip-fila, og spare innholdet i en katalog. Denne katalogen vil inneholde en masse filer. Selve strukturen i dokumentet finner du i content.xml.
Nedenfor skal vi bruke Python til å unzippe en .odt fil og inspisere innholdet. Hensikten er selvsagt å gjøre Open Office filer tilgjengelig som data for videre behandling.

Utpakking

Det første vi ønsker å få på plass er selve utpakkingen. Et Python script som gjør dette kan se slik ut:

_unzipodf.py
"""
Unzipping a ODT file into a catalog
"""
import sys, zipfile, os, os.path
def unzip_oo_file_to_dir(file, dir):
    result=''
    try:
        if not os.path.exists(dir):
            os.mkdir(dir)
    except:
        res=sys.exc_info()
        print res[1]
        
    zfobj = zipfile.ZipFile(file)
    # if we want to print all filenames found :
##    for name in zfobj.namelist():
##        print name
##    print'-------------------------------------------'
    for name in zfobj.namelist():
        fullpath=os.path.join(dir, name)
        fullpath=fullpath.replace('\\','/')
        pos=fullpath.rfind('/')
        if pos==-1:
            continue
        tpath=fullpath[0:pos]
        try:
            if not os.path.exists(tpath):
                os.makedirs(tpath)            
        except:
            res=sys.exc_info()
            print res[1]
    for name in zfobj.namelist():        
        if not name.endswith('/'):
            fullpath=os.path.join(dir, name)
            try:
                outfile = open(fullpath, 'wb')
                # stretch the content-files for readability
                if fullpath.find('content.xml')!=-1:
                    outfile = open(fullpath, 'w')
                    T=zfobj.read(name)
                    T=T.replace('><','>\n<')
                    T=T.replace(' xmlns','\nxmlns')
                    outfile.write(T)
                    outfile.close()
                else:
                    outfile = open(fullpath, 'wb')
                    outfile.write(zfobj.read(name))
                    outfile.close()
            except:
                res=sys.exc_info()
                print res[1]
if __name__ == '__main__':
    unzip_oo_file_to_dir('formula2.odt', 'unpacked\\')

Inspeksjon

Når fila er vel utpakket og alle delene er tilgjengelige er det selvsagt avhengig av den oppgaven vi har hva vi skal gjøre med innholdet. Her er et enkelt eksempel på et program som inspiserer innholdet, altså XML-strukturen.

_examine1.py
import unzipodf,utils
import os,os.path
import xml.dom.minidom
"""
Find the main content file
"""
def find_main_content(dir):
    filename=os.path.join(dir, 'content.xml')
    return utils.getTextFile(filename)
"""
Find the content for an referenced object
"""
def find_object_content(dir,objectpath):
    catname=os.path.normpath(os.path.join(dir,objectpath))
    obfile=os.path.join(catname,'content.xml')
    return utils.getTextFile(obfile)
"""
Find the type of object
"""
def find_object_type(contentDom):
    #look for a formula
    lst=contentDom.getElementsByTagName('math:math')
    if len(lst) > 0:
        return 'math'
    lst=contentDom.getElementsByTagName('office:chart')
    if len(lst) > 0:
        return 'chart'
    return 'unknown'
"""
Show headings
"""
def show_headings(contentDom):
    headings=contentDom.getElementsByTagName("text:h")
    for h in headings:
        if h.hasAttribute('text:outline-level'):
            print h.getAttribute('text:outline-level')+':'+utils.getText(h.childNodes)
        else:
            print 'what ?'
"""
Show objects
"""
def show_objects(contentDom):
    objects=contentDom.getElementsByTagName("draw:object")
    for obj in objects:
        fileref=obj.getAttribute('xlink:href')
        S=find_object_content(maindir,fileref)
        sDom= xml.dom.minidom.parseString(S)
        objType=find_object_type(sDom)
        print fileref+' is '+objType
"""
Show images
"""
def show_images(contentDom):
    imgs=contentDom.getElementsByTagName("draw:image")
    for im in imgs:
        if im.getAttribute('xlink:href').find('Pictures')!=-1:
            print im.getAttribute('xlink:href')
#---------------------------------------
if __name__ == '__main__':
    maindir='unpacked\\'
    # use the unzip module
    unzipodf.unzip_oo_file_to_dir('formula2.odt',maindir)
    # pick up main content, content.xml
    T=find_main_content(maindir)
    if T==None:
        print 'sorry'
        sys.exit(0)
    # establish DOM - tree
    try:
        dom = xml.dom.minidom.parseString(T)
    except:
        print 'sorry'
        exit(0)

    # look for interesting nodes
    print '---------- headings ----------'
    show_headings(dom)
    print '---------- objects -----------'
    show_objects(dom)
    print '---------- images -----------'
    show_images(dom)    
    # clean up
    dom.unlink()

Rensing

Hvis vi inspiserer en utpakket content.xml fil vil vi se at den inneholder en masse informasjon som er nødvendig for at Open Office skal kunne gjenskape fila med all nødvendig layout og alle stiler vi har definert. Det ser også ut til av det er lagt ut informasjon som skal brukes når Open Office skal lagre/eksportere fila i et annet format enn odt. Hvis vi ikke er interessert i dette kan vi prøve å rense fila slik at vi bare sitter igjen med selve strukturen og referanser til objekter som bilder og formler. Det er mange måter å gjøre dette på. En måte er å bruke et xslt-stilsett og transformere content.xml til en forenklet versjon. Et slikt stilsett som er brukt for effektiv analyse kan se slik ut, modifisert fra Silje Arendt Olsen[1] :

_cleaning.xslt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
        xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
        xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"
        xmlns:xlink="http://www.w3.org/1999/xlink"
        xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
        xmlns="http://www.w3.org/1999/xhtml"
        xml:lang="en"
    >
<xsl:output version="1.0" method="xml" indent="yes"
            encoding="UTF-8" omit-xml-declaration="no" />
 <!-- main -->
<xsl:template match="/">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta http-equiv="Content-Style-Type" content="text/css" />
        <title>Odt2Wiki - generated document</title>
    </head>
    <body class="mediawiki">
        <div id="globalWrapper">
        <div id="column-content">
         <xsl:apply-templates/>
        </div>
        </div>
    </body>
</html>
</xsl:template>
<!-- end main -->

<!-- lists -->
<xsl:template match="text:list">
<ul>
    <xsl:attribute name="class">odt-list</xsl:attribute>
    <xsl:apply-templates/>
</ul>
</xsl:template>
<!-- list items-->
<xsl:template match="text:list-item">
    <li><xsl:apply-templates/></li>
</xsl:template>
<!-- end lists -->
<!-- Headings -->
<xsl:template name="text1" match="text:h">
<!-- h1 -->
<xsl:if test="@text:outline-level='1'">
<h1 class="firstHeading"><xsl:apply-templates/></h1>
</xsl:if>
<!-- h2 -->
<xsl:if test="@text:outline-level='2'">
<h2><xsl:apply-templates/></h2>
</xsl:if>
<!-- h3 -->
<xsl:if test="@text:outline-level='3'">
<h3><xsl:apply-templates/></h3>
</xsl:if>
<!-- h4 -->
<xsl:if test="@text:outline-level='4'">
<h4><xsl:apply-templates/></h4>
</xsl:if>
<!-- h5 -->
<xsl:if test="@text:outline-level='5'">
<h5><xsl:apply-templates/></h5>
</xsl:if>
<!-- h6 -->
<xsl:if test="@text:outline-level='6'">
<h6><xsl:apply-templates/></h6>
</xsl:if>
</xsl:template>
<!-- end headings -->
<!-- objects -->
<xsl:template match="draw:frame" name="object">
 <xsl:choose>
    <xsl:when test="draw:object">
    <img>
        <xsl:attribute name="src"><xsl:value-of select="@xlink:href"/></xsl:attribute>
        <xsl:attribute name="class">object</xsl:attribute>
        <xsl:attribute name="title"><xsl:value-of select="../@draw:name" /></xsl:attribute>
        <xsl:attribute name="alt"><xsl:value-of select="../@draw:name" /></xsl:attribute>
    </img>        
    </xsl:when>
    <xsl:otherwise>
        <xsl:apply-templates/>
    </xsl:otherwise>
</xsl:choose>
 </xsl:template>
<!-- Tables -->
<xsl:template match="table:table">
<table>
    <xsl:attribute name="class">odt-table</xsl:attribute>
    <xsl:attribute name="id"><xsl:value-of select="@table:name"/></xsl:attribute>
    <xsl:apply-templates/>
</table>
</xsl:template>
<!-- Tables: row -->
<xsl:template match="table:table-row">
<tr>
    <xsl:apply-templates/>
</tr>
</xsl:template>
<!-- Tables: cell -->
<xsl:template match="table:table-cell">
<td>
    <xsl:apply-templates/>
</td>
</xsl:template>
<!-- end tables -->

<!-- Images-->
<xsl:template match="draw:image" name="image">
<img>
    <xsl:attribute name="src"><xsl:value-of select="@xlink:href"/></xsl:attribute>
    <xsl:attribute name="class">image</xsl:attribute>
    <xsl:attribute name="title"><xsl:value-of select="../@draw:name" /></xsl:attribute>
    <xsl:attribute name="alt"><xsl:value-of select="../@draw:name" /></xsl:attribute>
</img>
</xsl:template>
<xsl:template match="draw:a"><!-- Image links-->
<a>
    <xsl:attribute name="href"><xsl:value-of select="@xlink:href"/></xsl:attribute>
    <xsl:apply-templates/>
</a>
</xsl:template>
<!-- end images -->
<!-- Links -->
<xsl:template match="text:a">
<a>
    <xsl:attribute name="class">odt-ref</xsl:attribute>
    <xsl:attribute name="href"><xsl:value-of select="@xlink:href"/></xsl:attribute>
    <xsl:attribute name="title"><xsl:value-of select="."/></xsl:attribute>
    <xsl:value-of select="."/>
</a>
</xsl:template>
<!-- end links -->
<!-- bookmarks -->
<xsl:template match="text:bookmark">
<a>
    <xsl:attribute name="class">odt-name</xsl:attribute>
    <xsl:attribute name="name"><xsl:value-of select="@text:name"/></xsl:attribute>
    <xsl:attribute name="id"><xsl:value-of select="@text:name"/></xsl:attribute>
</a>
</xsl:template>
<!-- end bookmarks -->

<!-- break line-->
<xsl:template match="text:line-break">
    <br /><xsl:apply-templates/>
</xsl:template>
<!-- end break line -->
<!-- text paragraphs -->
<xsl:template match="text:p[ .!='' ]">
<p>
    <xsl:apply-templates/>
</p>
</xsl:template>
<!-- end text paragraphs -->
</xsl:stylesheet>
Referanser
  1. Transformasjon av OpenOffice content file til enkel XML Arendt Olsen,Silje HiØ Komponent i hovedprosjekt informatikk bachelor
  1. Python Swaroop CH www.swaroopch.com/notes/Python 14-08-2011
Vedlikehold

B.Stenseth, oktober 2010

( Velkommen ) Python >Open Office ( ting og tang )