Thursday, November 18, 2010

A Progress Report on Self Displaying CDA

As promised, here is a progress report on Self Displaying CDA. Now, the whole premise of this project is that the XML, CSS and CDA standards are sufficient to embed a CSS stylesheet inside a CDA document so that you can display it automagically in a browser.

So, let's take a look at what that would require:
  1. A CDA document. OK, I have plenty of those.
  2. An XML Stylesheet Processing Instruction which uses just a fragment identifier to indicate the location of the element containing the stylesheet.  It looks like this:
    ‹?xml-stylesheet href='#css' type='text/css'?›
  3. An element with an ID containing a CSS stylesheet.
  4. A browser that knows how to apply CSS to XML (I have plenty of those).
Put it all together, and if everyone implements the standards, it should work.  Right?  Right.

So, what doesn't work?
Well, my proof of concept starts with a pair of demonstration files for Displaying XML with CSS.  I simply added an element at the very end of the catalog.xml document from that site (* see note below):
  ‹css ID='css'›
  ‹/css›

Then I inserted the CSS stylesheet content inside it, fired up IE 8 and viewed the result.  It worked.  Then, I tried this test in four other browsers.  Firefox succeeded, but Opera, Safari and Chrome failed.  And Opera made my **** list for overriding my browser preference without asking.

Without looking at the source code (because that would be cheating -- but which you can get for some of the engines these browsers are using), I tried to figure out if there was something I was doing wrong.  And there was.  The problem is with href="#css" in the Stylesheet PI.  That's a relative URI to the "current resource", that points to the element identified by the ID (type not attribute) having the value "css".  But, there's no way for the XML Parser to really know that the ID attribute on the CSS element is of the xs:ID type.  FireFox and IE are just guessing.  And unless the parser knows that the element is of the xs:ID type, it cannot reference the URL.  And because specifying the CDA schema location isn't legal according to the HL7 XML ITS, there's no way to tell the parser how to find the schema so that it can figure this out.  So that's a stalemate.

But I recalled that the W3C had a fix for this, at least in the DTD for XML Schema (put that in your parser and validate it), where they included ATTLIST declarations for all the XML Schema elements using the ID attribute.  So, I know a way around this (or so I thought).  I could include a DOCTYPE declaration that will tell the parser that the element was of the ID type, just like the W3C did.  So I very carefully added a DOCTYPE declaration, and put it after my XML declaration as required by the XML standard, and before the stylesheet PI so that the parser would know that the ID attribute on the css element was of the type ID BEFORE it tried to process the stylesheet PI.  Like you see below:

‹?xml version="1.0" encoding="ISO-8859-1"?›
‹!DOCTYPE CATALOG [‹!ATTLIST css ID ID #IMPLIED› ]›
‹?xml-stylesheet href='#css' type='text/css'?›

That failed to work either, and was also a little bogus including a DOCTYPE that wouldn't validate the XML, but only defined an ID attribute for one element.  Then I was wishing that W3C had defined the xml:ID attribute in the XML standard.  They didn't, but they did define xml:id later.  OK, so now I have something else to try, but I'm betting it won't work either... 5 minutes later ... NOPE, that failed too in the other browsers, but at least it still works in IE and FireFox.

But that at least gives me a rationale for what SHOULD be done inside the CDA document, maybe.  And it also says something else about what should happen to ID attributes that were manually added to the CDA schema.  They may need to use xml:id instead.  There's a good reason to do it that way.

Anyway, I'm stuck with IE 8.0 and Firefox, at least until someone fixes WebKit (for Safari and Chrome) or Opera. 

So, the first question this Guide needs to answer is which attribute to use to identify the stylesheet.  Should it be an xml:id attribute, or should it be the CDA ID attribute on the ... OH DANG ... and then I realize that there's a placement problem. 

The ID attribute appears on the ‹observationMedia› element containing the ‹value› element containing the stylesheet, instead of on the ‹value› element directly.  This is ONE element too far away from the stylesheet (and as it turns out THAT DOESN'T matter to the two browsers I'm stuck with here, but I'd like to be legit).  The figure below illustrates the problem.  The one below marked notLegal is where the attribute really SHOULD (shall?) go.

‹observationMedia ... ID='legal'
  ‹value mediaType="text/css" representation="TXT" ID='notLegal'
‹/observationMedia›

OK, so here's a thought.  If we use xml:id, it's already a legal CDA extension, and can go right on the ‹value› element just fine.  Well, as long as the CDA producer and reciever already deals with legal CDA extensions.  And that puts it in the right place instead of one element above the right place.  But now that becomes a different problem because it puts an extension in the mix.  Well, that's one to save for committee discussion.

So the next big problem is the table box model and whether or not I can get it to work on both Browsers.  There's a work-around if it doesn't work... which is simply to say that Self-Displaying-CDA documents either DON'T contain tables, or contain padding and use monospaced fonts for display and use the block rather than table display styles. 

The CSS stylesheet?  What?  You want the CSS stylesheet?  That's pretty easy, but also needs more work to deal with the styleCode attribute, which it doesn't handle just yet, and a half dozen other details.  What follows is what I have so far, and it's still pretty slim, about 4K.  The commented out table display types are what IE DOESN'T seem to support.

paragraph, addr, text { display: block; }
ClinicalDocument { display: block; }
#css             { display: none; }
entry            { display: none; }
name             { display: block; }
section›text     { display: block; }
table            { display: block; /* table; */border-spacing: 2px; }
tr               { display: block; /* table-row;*/ vertical-align: inherit }
thead            { display: block; /* table-header-group; */ vertical-align: middle  }
tbody            { display: block; /* table-row-group; */ vertical-align: middle  }
tfoot            { display: block; /* table-footer-group; */ vertical-align: middle  }
col              { display: table-column; }
colgroup         { display: table-column-group; }
td               { display: inline-block; /* table-cell; */ vertical-align: inherit; }
th               { display: inline-block; /* table-cell; */ font-weight: bolder; text-align: center; vertical-align: inherit }
table›caption    { display: block; /* table-caption; */ text-align: center; }

structuredBody   { margin: 8px; display: block; }

ClinicalDocument›title { font-size: 2em; margin: .67em 0 }
structuredBody›component›section›title { font-size: 1.5em; margin: .75em 0; }
structuredBody›component›section›component›section›title { font-size: 1.17em; margin: .83em 0 }
structuredBody›component›section›component›section›component›section title { margin: 1.12em 0 } 
structuredBody›component›section›component›section›component›section›component›section›title { font-size: .83em; margin: 1.5em 0 }
structuredBody›component›section›component›section›component›section›component›section›component›section›title { font-size: .75em; margin: 1.67em 0 }
title           { padding-top: 10px; font-weight: bolder; display: block;  }

addr            { font-style: italic }
sub             { font-size: .83em; vertical-align: sub }
sup             { font-size: .83em; vertical-align: super }

list            { margin: 1.12em 0; margin-left: 40px;  display: block;  }
list [listType="ordered"] { margin: 1.12em 0; margin-left: 40px;  display: block;  list-style-type: decimal; }
list [listType="unordered"] { margin: 1.12em 0; margin-left: 40px;  display: block;  list-style-type: decimal; }
item             { display: list-item; }
list›list       { margin-top: 0; margin-bottom: 0 }
br:before       { content: "\A"; white-space: pre-line }
streetAddressLine:before, 
city:before
country:before  { content: "\A"; white-space: pre-line }
center          { text-align: center }
:link, :visited { text-decoration: underline }
:focus          { outline: thin dotted invert }

So, how do you use this?  You create an observationMedia element in any section of your CDA document.  In the value element, you use mediaType='text/css', representation='TXT' and xml:ID='css'.  Then you include the above stylesheet text inside the value element and add the appropriate stylesheet PI (see the top of this page).

There are still some details to work out.  That PI should be an "Alternate" stylesheet so the browser won't barf and so you can offer up an XSLT based one for use.  There should be a document template in the CDA to identify it as conforming to the rules for "self-displaying-cda" (or maybe there are a couple of them with different degrees of conformance ... haven't gotten there yet, but table support or not leads me to think that there are degrees).

Oh, and sorry, but no embedded images in Self-Displaying-CDA.  There's just no way to handle them.  XSLT can work, but not CSS ... yet.

To follow the development more closely, check out the HL7 Wiki pages for the project.


* Code samples above use French quote symbols instead of real less than and greater than symbols around the XML because Blogger just doesn't know how to deal with XML markup symbols in Compose mode.  Just search and replace them with the right characters if you copy and paste the code from the blog.

2 comments:

  1. Thanks Keith. Obviously this means we should use xml:id next release of CDA? (Actually, we might by default because RIM ITS uses xml:id as a target)

    What impact would CDA using XHTML have on this?

    ReplyDelete
  2. Oh Foo. I wish the W3C was consistent. They DIDN'T use xml:id in XHTML.

    ReplyDelete