08 February 2011

Export all Tomboy notes into HTML/XHTML

Just wrote Python script to export all the Tomboy notes into an HTML file. The following script connects Tomboy via DBus Interface, creates XHTML file and converts it to HTML by means of an XSL. Conversion powered by xsltproc utility.

tomboy2html.py

#!/usr/bin/python
# Copyright (C) 2011 - Ruslan Osmanov <rrosmanov@gmail.com>
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
# See <http://www.gnu.org/licenses/>
# 
##
# @file tomboy2html.py
# @brief Export all Tomboy notes in XHTML/HTML file(s)
# @author Ruslan Osmanov
# @version 1.0
# @date 08.02.2011
# @details xsltproc utility required
# @copyright Copyright (C) 2011 - Ruslan Osmanov

import dbus, dbus.glib
try:
  import gobject
except ImportError:
  # gobject functions are moved to dbus.glib in this Python version
  pass
import os, sys, getopt
import re

def usage():
  print __file__ + " [OPTIONS]"
  print """Options:
-h, --help    Display help
-p, --prefix    Optional. Output filename prefix. Default: notes
-x, --xhtml   Optional. Generate XHTML file also. Default: off 
-s, --xsl   Optional. XSL for XML-HTML conversion. Default: tomboy-notes.xsl 
-d, --debug   Optional. Debug mode. Default: off 
"""

def main(argv):
  debug_mode = False
  prefix = 'notes'
  xhtml = False
  xsl_filename = os.path.dirname(__file__) + '/tomboy-notes.xsl'

  # Get CLI options
  try:
    opts, args = getopt.getopt(argv[1:], "s:p:xhd", 
        ("xsl=", "prefix=", "debug", "xhtml", "help"))

  except getopt.GetoptError:
    usage()
    sys.exit(2)

  # Save CLI options
  for o, a in opts:
    if o in ('-h', '--help'):
      usage();
      sys.exit();

    elif o in ('-x', '--xhtml'):
      xhtml = True

    elif o in ('-d', '--debug'):
      debug_mode = True

    elif o in ("-p", "--prefix"):
      prefix = os.path.expanduser(a)

    elif o in ("-s", "--xsl"):
      xsl_filename = os.path.expanduser(a)

  xhtml_filename = prefix + '.xhtml'
  html_filename = prefix + '.html'
  if False == os.path.exists(xsl_filename):
    print "XSL file '"+xsl_filename+"' doesn't exist"
    sys.exit(2)

  if debug_mode:
    print "xhtml =", xhtml_filename, "\nhtml =", html_filename
    print "\nxsl =", xsl_filename

  # Access the Tomboy remote control interface
  bus = dbus.SessionBus()
  obj = bus.get_object("org.gnome.Tomboy", "/org/gnome/Tomboy/RemoteControl")
  tomboy = dbus.Interface(obj, "org.gnome.Tomboy.RemoteControl")

  # Get note URIs
  all_notes = tomboy.ListAllNotes()

  # Write each note XML to the XHTML file
  f = open(xhtml_filename, "w")
  f.write('<?xml version="1.0" encoding="utf-8"?>' + 
      '<?xml-stylesheet type="text/xsl" href="' +xsl_filename+'"?>'
      '<notes>')
  for n in all_notes:
    if (debug_mode):
      print "NOTE '"+n+"'"
    xml = re.sub(r'<\?xml[^<]*\?>', '', tomboy.GetNoteCompleteXml(n))
    xml = re.sub(r'\&\#x?.*\;', '', xml)
    f.write(unicode(xml).encode("utf-8"))

  f.write('</notes>')
  f.close()

  # Generate HTML
  cmd = "xsltproc -o '%(html)s' '%(xsl)s' '%(xhtml)s'" % \
      {'html': html_filename.replace("'", "\\'"), 
          'xsl': xsl_filename, 
          'xhtml': xhtml_filename}
      if debug_mode:
        print cmd
  os.system(cmd)

  if xhtml == False:
    os.unlink(xhtml_filename)

if __name__ == "__main__":
    main(sys.argv)

tomboy-notes.xsl

<?xml version='1.0' encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tomboy="http://beatniksoftware.com/tomboy"
xmlns:notes="http://beatniksoftware.com/tomboy/notes"
xmlns:size="http://beatniksoftware.com/tomboy/size"
xmlns:link="http://beatniksoftware.com/tomboy/link"
version='1.0'>

<xsl:output method="html" indent="no" />
<xsl:preserve-space elements="*" />

<xsl:param name="font" />
<xsl:param name="export-linked" />
<xsl:param name="export-linked-all" />
<xsl:param name="root-note" />

<xsl:param name="newline" select="'&#xA;'" />

<xsl:template match="/">
<html>
<head>
    <title>Tomboy Notes Export</title>
    <style type="text/css">/*<![CDATA[*/
        body { <xsl:value-of select="$font" /> }
        h1 { 
            font-size: xx-large;
            font-weight: bold;
            border-bottom: 1px solid black; 
        }
        div.note {
            position: relative;
            display: block;
            padding: 5pt;
            margin: 5pt;
            white-space: -moz-pre-wrap; /* Mozilla */
            white-space: -pre-wrap; /* Opera 4 - 6 */
            white-space: -o-pre-wrap; /* Opera 7 */
            white-space: pre-wrap; /* CSS3 */
            word-wrap: break-word; /* IE 5.5+ */ 
        }
        table.note-info{
            margin: 20px 0 5px;
            font-size:xx-small;
            border-width: 0 1px 1px 0;
            border-style: solid;
        }
        table.note-info td{
            padding: 2px 5px;
            border-width: 1px 0 0 1px;
            border-style: solid;
        }
        /*//]]>*/
    </style>
</head>
<body>
    <xsl:for-each select="notes/tomboy:note">
    <div class="note">
        <xsl:apply-templates select="tomboy:text | tomboy:title"/>

        <table class="note-info" boder="0" cellpadding="0" cellspacing="0"
            summary="Note Info">
            <tr>
                <td>Last updated:</td>    
                <td><xsl:value-of select="tomboy:last-change-date" /></td>
            </tr>    
            <tr>
                <td>Tags:</td>    
                <td>
                    <xsl:for-each select="tomboy:tags">
                    <div><xsl:value-of select="tomboy:tag"/></div>    
                    </xsl:for-each>
                </td>
            </tr>    
        </table>
    </div>
    </xsl:for-each>
</body>
</html>
</xsl:template>



<xsl:template match="text()">
    
    <xsl:value-of select="."/>
</xsl:template>

<xsl:template match="child::tomboy:text">
    <xsl:apply-templates select="node()"/>
</xsl:template>

<xsl:template match="tomboy:title">
    <h1><xsl:value-of select="text()"/></h1>
</xsl:template>
<xsl:template match="tomboy:bold">
    <strong><xsl:apply-templates select="node()"/></strong>
</xsl:template>
<xsl:template match="tomboy:italic">
    <i><xsl:apply-templates select="node()"/></i>
</xsl:template>
<xsl:template match="tomboy:monospace">
    <code><xsl:apply-templates select="node()"/></code>
</xsl:template>

<xsl:template match="tomboy:strikethrough">
<strike><xsl:apply-templates select="node()"/></strike>
</xsl:template>

<xsl:template match="tomboy:highlight">
<span style="background:yellow"><xsl:apply-templates select="node()"/></span>
</xsl:template>

<xsl:template match="tomboy:datetime">
<span style="font-style:italic;font-size:small;color:#888A85">
<xsl:apply-templates select="node()"/>
</span>
</xsl:template>

<xsl:template match="size:small">
<span style="font-size:small"><xsl:apply-templates select="node()"/></span>
</xsl:template>

<xsl:template match="size:large">
<span style="font-size:large"><xsl:apply-templates select="node()"/></span>
</xsl:template>

<xsl:template match="size:huge">
<span style="font-size:xx-large"><xsl:apply-templates select="node()"/></span>
</xsl:template>

<xsl:template match="link:broken">
<span style="color:#555753;text-decoration:underline">
<xsl:value-of select="node()"/>
</span>
</xsl:template>
<xsl:template match="link:internal">
<a style="color:#204A87" href="#{node()}">
<xsl:value-of select="node()"/>
</a>
</xsl:template>

<xsl:template match="link:url">
<a style="color:#3465A4" href="{node()}"><xsl:value-of select="node()"/></a>
</xsl:template>

<xsl:template match="tomboy:list">
<ul>
<xsl:apply-templates select="tomboy:list-item" />
</ul>
</xsl:template>

<xsl:template match="tomboy:list-item">
<li>
<xsl:if test="normalize-space(text()) = ''">
<xsl:attribute name="style">list-style-type: none</xsl:attribute>
</xsl:if>
<xsl:attribute name="dir">
<xsl:value-of select="@dir"/>
</xsl:attribute>
<xsl:apply-templates select="node()" />
</li>
</xsl:template>

<xsl:template match="//tomboy:x | //tomboy:y | //tomboy:width | //tomboy:height | //tomboy:cursor-position | //tomboy:create-date | tomboy:last-change-date | tomboy:open-on-startup | tomboy:last-metadata-change-date | tomboy:tags">
<xsl:comment>literal</xsl:comment>
</xsl:template>

</xsl:stylesheet>

Usage

I saved the files here in ~/scripts/python/tomboy2html/. Thus, to save all the Tomboy notes in all_notes.xhtml and all_notes.html, I can issue the following:
$ chmod +x ~/scripts/python/tomboy2html/tomboy2html.py
$ ~/scripts/python/tomboy2html/tomboy2html.py -p "all_notes" -x -s \
~/scripts/python/tomboy2html/tomboy-notes.xsl
It creates all_notes.xhtml and all_notes.html files in current directory.

Update 09 February 2011

Clone the Git repo:
$ git clone git://github.com/rosmanov/Tomboy2HTML.git