Extracting CML from a Chem4Word authored document (C#)

January 21, 2010

My previous post was the first in a series demonstrating how CML embedded in DOCX files could be extracted (in that case using Java). For completeness I thought I ought to post some code to accomplish the same thing in C#. This should also allow people to get used to the packaging tools before we build up functionality in later posts.

If you would like a file containing CML to test this out with, one is available here.

Now for the code:


using System.Collections.Generic;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;

namespace Chem4Word.Tools 
{
  public class OOXMLTools 
  {
    public ICollection GetCML(string path) 
    {
      ICollection list = new List();
      using (Package package = Package.Open(path, FileMode.Open)) 
      {
        foreach (PackagePart packagePart in package.GetParts()) 
        {
          if (packagePart.ContentType == "application/xml") 
          {
            using (StreamReader streamReader = 
               new StreamReader(packagePart.GetStream())) 
            {
              try 
              {
                XDocument xDocument = 
                  XDocument.Parse(streamReader.ReadToEnd());
                if (xDocument.XPathSelectElements(
              "//*[local-name()='cml' and namespace-uri()=
              'http://www.xml-cml.org/schema']").Count() > 0) 
                {
                  list.Add(xDocument);
                }
              }
              catch 
              {
                  // not valid XML so therefore can't be CML
              }
            }
          }
        }
      }
      return list;
    }
  }
}

So there we go. Pretty similar to the Java version really. Just in case you were wondering, I know I haven’t done a load of exception checking.


Extracting CML from a Chem4Word authored document (Java)

January 20, 2010

I have been meaning to write this post for ages and thanks to a recent tweet from Egon I’ve finally got round to it. Basically what I am going to do over a series of posts is explain how CML can be extracted from a DOCX (OOXML) file authored using Chem4Word. I’ll post methods in both Java and C# but I am starting off in Java.

A very quick into to DOCX and OOXML

There are plenty of blogs, papers, videos and the like out there which explain OOXML in various levels of detail. I don’t want to replicate that here but I think it will be useful to have a quick overview for reference. Microsoft developed the Open Packaging Convention (OPC) specification as a successor to its binary Microsoft Office file formats. The file-extension DOCX indicates an OPC document which should be edited using Microsoft Office Word 2007 (as opposed to the XSLX file extension for example which are OPC documents editable using Excel). A DOCX document is effectively a zip-file (the package) which contains the original text as a marked-up XML component (document.xml), with images and other embedded objects stored as separate files.

the simplified structure of an OPC document

The package-part word\document.xml contains the main text and body of the document. Chem4Word stores CML files in the customXml folder within the package. This directory contains pairs of files with names item[\d].xml and itemProps[\d].xml – itemProps[n] contains a list of all the namespaces and schemas used in item[n].

Getting the CML out – the brute force extraction method

The first method of extracting the CML files is the simplest. This method does not allow us to know anything more about the data other than it has been included somewhere in the document by the user. For example, we don’t know where and how it is being used in the document (or how many times). So, for the algorithm: iterate through all those files where the CML may be found, attempt to build each file as a XOM document, if it builds then search within it for a cml element from the cml namespace (see code below).

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import nu.xom.Builder;
import nu.xom.Document;

public class OOXMLTools {

  public static List<Document> GetCML(File file)
    throws ZipException, IOException  {

    ZipFile zipFile = new ZipFile(file);
    List<Document> list = new ArrayList<Document>();
    Builder builder = new Builder();
    Matcher m =
      Pattern.compile("customXml/item([1-9][0-9]*)\\.xml")
        .matcher("");

    for (Enumeration <? extends ZipEntry>
      entries = zipFile.entries(); entries.hasMoreElements();) {

      ZipEntry entry = entries.nextElement();
      if (m.reset(entry.getName()).matches()) {
        try {
          Document doc =
            builder.build(zipFile.getInputStream(entry));

          if (doc.query("//*[local-name()='cml' and
            namespace-uri()='http://www.xml-cml.org/schema']")
              .size() > 0) {

            list.add(doc);
          }

        } catch (Exception e) {
          // not an XML file so can't be CML
        }
      }
    }
    return list;
  }
}

If you would like to try it out here is a DOCX file with some chemistry in it. There are three chemistry zones in the document (containing testosterone [item1] and acetic acid [item3]) but only two CML files in the customXml directory because both testosterone instances point to the same backing CML file.

In following posts I will go further into how you can discover which representation is being used in the document, how many times a particular CML file is referenced and how the data is converted into the on screen representation.


Using Mercurial, VisualHG and Visual Studio 2008 together

December 16, 2009

There is a strong desire in our group to move towards distributed version control systems specifically mercurial. As I spend most of my time working on the Chemistry Add-in for Word 2007 (Chem4Word) I have been using Visual Studio 2008. This doesn’t come with source control support out of the box – but you can (and we have) added on team foundation server support but this isn’t distributed and quite frankly sucked.

I have spent the last couple of days investigating other options and having got a good solution up and running thought I should share this with the world. This definitely isn’t meant to be an introduction to mercurial in general and I won’t be explaining all the terms (good intros are available from the mercurial website and elsewhere). I will be giving both command line and GUI instructions.

Installing VisualHG

The instructions on the VisualHG site are pretty good – but you only need to go through the first three. Basically you need to download and install TortoiseHg and then VisualHG.

Next make VisualHG the current active Source Control Plugin in Visual Studio 2008. <Tools> -> <Options> -> <Source Control>. Set the value of the drop-down list (Current Source Control plug-in) to <VisualHG>.

How to create a new repository and add it to source control

Open Visual Studio 2008 and create a new solution (in my case I went for a Console Application called cs-walkthrough under C:\projects\c# and I didn’t want a new directory for the solution)

In a command window change directory so you are in cs-walkthrough.

>hg init

this will create the .hg directory

create a file called .hgignore as a sibling of the .hg directory the contents of the file should be:

syntax: glob

*.csproj.user
obj/
bin/
*.ncb
*.suo
_ReSharper.*
*.resharper.user

Create a project on bitbucket with the same name as the solution (doesn’t have to be but will make things easier). So in this case I now have a publicly viewable repository on bitbucket http://bitbucket.org/jat45/cs-walkthrough.

Now create a file called hgrc under the .hg directory. This file should contain the following text:

[paths]
default = http://bitbucket.org/jat45/cs-walkthrough

[ui]
username = Joe Townsend

where jat45 should be replaced with your bitbucket id and cs-walkthrough should be replaced by the name of the bitbucket repository you just created.

From here on in you can do everything either from the command line or from within Visual Studio. I will give both commands (to access the VisualHG commands right-click on any of the files in the solution). All the command line commands should be run from the [cs-walkthrough] directory.

>hg status

hg status from the command line

or <HG status> from VisualHG

hg status from VisualHG

We want all these files under source control so either use

>hg add
or select all the files from the GUI and click Add then close the window

To commit these additions either use
>hg commit -m "initial commit"
or commit using <HG Commit>

Now push these changes to bitbucket

>hg push
or <HG Synchronize>(this will allow you to push, pull and see the incoming and outgoing changes).

Warning

Before committing or pushing changes you must make sure you build the solution first. This ensures that the [project].csproj file is correctly updated.

Getting an existing project

This is much easier than creating a new repository from scratch. Once again though we are back to the command line.

Change directory to the parent of where you would like the new project repository to be checked out to. In my case this is c:\projects\c#.

I am now going to get a clone of the cs-walkthough code from bitbucket but put it in a new location.

c:\projects\c#>hg clone http://bitbucket.org/jat45/cs-walkthrough my-new-source-dir

You can use the same command because the project is public so you can access the source code but not commit.

So I think that is it. You can do all the usual, push, pull, diff and so on either from the command line or from the VisualHG add-in. The one only thing that must be done from the command line is the initial clone or init. I am also investigating if everything works in VS2010 – so far no problems but if I run into any I will update you.


Follow

Get every new post delivered to your Inbox.