Taoffi's blog

prisonniers du temps

OneNote page xsl transformation

As we saw in a previous post, OneNote API is xml-based. You call the OneNote Application object to obtain almost all needed information as xml strings.

One of that information is the page content. Whose schema is defined as explained in the previous post.

Once you get the page content’s xml string, it is a little bit of work to transform that into a useful html page

To do this, I used an xslt style sheet which is the subject of the current post.

How does it work?

Assume you have an xsl sheet string and the xml string of a page. You can then process both in a way similar to the following code:

 

using System.Xml.Xsl;

 

public static string XmlToHtml(string xmlString, string xslString)
{
    string            html;
    XslCompiledTransform    transform  = null;
    XmlReader         xslReader        = null,
                      xmlReader        = null;
    MemoryStream      memStream        = null;
    StreamReader      sr               = null;
    MemoryStream      xslStream        = null,
                      xmlStream        = null;
    byte[]            xslBytes         = Encoding.UTF8.GetBytes( xslString),
                      xmlBytes         = Encoding.UTF8.GetBytes( xmlString);

    xslStream    = new MemoryStream( xslBytes);
    xmlStream    = new MemoryStream( xmlBytes);

    transform    = new XslCompiledTransform();
    xslReader    = XmlReader.Create( xslStream);
    xmlReader    = XmlReader.Create( xmlStream);
    memStream    = new MemoryStream();

    transform.Load( xslReader);
    transform.Transform( xmlReader, null, memStream);

    memStream.Position    = 0;

    sr      = new StreamReader( memStream);
    html    = sr.ReadToEnd();
               
    xslStream.Close();
    xmlStream.Close();
    memStream.Close();
    sr.Close();
    xslReader.Close();

    return html;
}

 

 

 

How to get the page xml content?

Assuming you have the page ID, here is a sample code to query the page content through OneNote API:

 

public static string GetPageContentXmlString(string pageId)
{
    var         onenoteApp  = new Application();
    string      pageXml     = null;
    PageInfo    pageInfo    = PageInfo.piAll;

    onenoteApp.GetPageContent(pageId, out pageXml, pageInfo);
    return pageXml;
}

 

Reminder: The page content xsd schema

 

The xsl style sheet general structure

Let us use the iXml explorer to navigate through the xsl stylesheet used to transform the page’s xml into html.

(I searched for ‘match’ to locate templates defined in the stylesheet).

As you may notice, we have an xsl template to handle each OneNote page defined xsd type:

  • A template to process the page root information
  • A template to process the page’s title
  • A template to process outline elements
  • A template to process OEChildren collection
  • … and so forth

Sample xsl code

Process OneNote Element (one:OE)

  <!--
  ************************************************
  one:OE
  ************************************************
  -->
  <xsl:template match="one:OE">
    <xsl:param name="nest_level" select="0" />

    <xsl:variable name="listNode" select="./one:List" />
    <xsl:variable name="quickStyleIndex" select="./@quickStyleIndex" />
    <xsl:variable name ="styleNode" select="msxsl:node-set($quickStyleList)/quickStyle[@index=$quickStyleIndex]" />

    <xsl:variable name="quickStyle">
      <xsl:choose>
        <xsl:when test="$styleNode">
          <xsl:value-of select="$styleNode/@style"/>
        </xsl:when>
      </xsl:choose>
    </xsl:variable>

    <!-- is there any list here? -->
    <xsl:choose>
      <xsl:when test="$listNode">
        <xsl:variable name="number"    select="$listNode/one:Number" />
        <xsl:variable name="txt"      select="./one:T" />
        <xsl:variable name="listItemTag">
          <xsl:text>li</xsl:text>
        </xsl:variable>

        <!-- list tag: either <ol> or <ul> -->
        <xsl:variable name="listTag">
          <xsl:choose>
            <xsl:when test="$number">
              <xsl:text>ol</xsl:text>
            </xsl:when>
            <xsl:otherwise>
              <xsl:text>ul</xsl:text>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:variable>

        <xsl:choose>
          <!-- numbered list? output <ol> -->
          <xsl:when test="$number">
            <xsl:variable name="txtNum"        select="number($number/@text)" />
            <xsl:variable name="fontNum"      select="$number/@font" />
            <xsl:variable name="tag" select="concat('<', $listTag, '>')" />
            <xsl:value-of disable-output-escaping="yes" select="$tag"/>

            <!-- *********** output <li value="xx"> ' style="font-family:', $fontNum, ';"',************* -->
            <xsl:value-of disable-output-escaping="yes" select="concat('<', $listItemTag, ' value=', '"', $txtNum, '">')" />

            <xsl:call-template name="outputListItem">
              <xsl:with-param name="listNode" select="$listNode" />
              <xsl:with-param name="itemText" select="$txt" />
            </xsl:call-template>
          </xsl:when>

          <!-- bullet ? output <ul> -->
          <xsl:otherwise>
            <xsl:variable name="tag" select="concat('<', $listTag, '>')" />
            <xsl:value-of disable-output-escaping="yes" select="$tag"/>
            <!-- *********** output <li> ************* -->
            <xsl:value-of disable-output-escaping="yes" select="concat('<', $listItemTag,'>')" />

            <xsl:call-template name="outputListItem">
              <xsl:with-param name="listNode" select="$listNode" />
              <xsl:with-param name="itemText" select="$txt" />
            </xsl:call-template>
          </xsl:otherwise>

        </xsl:choose>

        <!-- process list's sub items -->
        <xsl:apply-templates select="./one:OEChildren">
          <xsl:with-param name="nest_level" select="1 + $nest_level" />
        </xsl:apply-templates>

        <xsl:apply-templates select="./one:Table" />

        <!-- close the list item tag -->
        <xsl:value-of disable-output-escaping="yes" select="concat('</', $listItemTag,'>')" />
        <!-- close the list tag -->
        <xsl:value-of disable-output-escaping="yes" select="concat('</', $listTag, '>')"/>
      </xsl:when>

      <!-- no list: process all sub items -->
      <xsl:otherwise>
        <xsl:variable name="style0" >
          <xsl:call-template name="string-replace-all">
            <xsl:with-param name="text" select="./@style" />
            <xsl:with-param name="replace" select="''" />
            <xsl:with-param name="by" select="''" />
          </xsl:call-template>
        </xsl:variable>

        <xsl:variable name="alignment" select="./@alignment" />

        <xsl:variable name="style">
          <xsl:choose>
            <xsl:when test="$alignment='right'">
              <xsl:value-of select="concat('width:100%; float:right; display:inline; text-align:right;', $style0)"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="$style0"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:variable>

        <xsl:choose>
          <xsl:when test="string-length($style)>0">
            <span style="{$style}">
              <xsl:apply-templates />
            </span>
          </xsl:when>

          <!-- ****** no style defined -->
          <xsl:otherwise>
            <xsl:choose>
              <xsl:when test="$quickStyle">
                <span style="{$quickStyle}">
                  <xsl:apply-templates>
                    <xsl:with-param name="nest_level" select="1 + $nest_level" />
                  </xsl:apply-templates>
                </span>
              </xsl:when>

              <xsl:otherwise>
                <span>
                  <xsl:apply-templates>
                    <xsl:with-param name="nest_level" select="1 + $nest_level" />
                  </xsl:apply-templates>
                </span>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:otherwise>
          <!-- ****** end of : no style defined -->
        </xsl:choose>

      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

Output a list (either numbered or bulleted)

<!--
  ************************************************
  output list item (numbered / bulleted)
  ************************************************
  -->
  <xsl:template name="outputListItem" match="one:List">
    <xsl:param name="listNode" />
    <xsl:param name="itemText" />

    <xsl:variable name="number"        select="$listNode/one:Number" />
    <xsl:variable name="bullet"        select="$listNode/one:Bullet" />
    <xsl:variable name="txtNum"        select="number($number/@text)" />

    <xsl:choose>
      <xsl:when test="$number">
        <xsl:value-of select="normalize-space($itemText)" disable-output-escaping="yes"/>
      </xsl:when>

      <xsl:otherwise>
        <xsl:value-of select="normalize-space($itemText)" disable-output-escaping="yes"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

 

Process page images

In the page’s xml, images are returned as base64 strings.

To process an image into html, the xsl template looks like the following:

  <!--
  ************************************************
  one:Image
  ************************************************
  -->
  <xsl:template match="one:Image">
    <xsl:variable name="imgWidth"select="substring-before( number(./one:Size/@width) * 1.33, '.')"/>
    <xsl:variable name="imgHeight" select="substring-before( number(./one:Size/@height) * 1.33, '.')"/>
    <xsl:variable name="imgData"    select="./one:Data" />
    <xsl:variable name="oneFormat"  select="./@format" />
    <xsl:variable name="htmlformat">
      <xsl:choose>
        <xsl:when test="$oneFormat='png'">
          <xsl:text>data:image/png;base64</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text>data:image/jpg;base64</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <xsl:variable name="htmlWidth">
      <xsl:choose>
        <xsl:when test="$imgWidth">
          <xsl:value-of select="$imgWidth"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text>96%</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <xsl:variable name="htmlHeight">
      <xsl:choose>
        <xsl:when test="$imgHeight">
          <xsl:value-of select="$imgHeight"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text>auto</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>


    <img width="{$htmlWidth}" height="{$htmlHeight}" src="{$htmlformat}, {$imgData}"/>
  </xsl:template>

 

Download the xsl stylesheet

You may download the entire xsl stylesheet (for OneNote 2013 and 2016) here

OneNote Explorer

In January 2004, Chris Pratley wrote about "OneNote genesis". His article ended by this sentence: "…I can see how this might become addictive."

Yes, as many people who know OneNote often use the word, "addictive" is a correct adjective for OneNote.

What makes it addictive is probably the fact that it is a medium for 'not-yet-documented' ideas. In the same time, it offers a good and simple hierarchical storage that helps organize those ideas for future documentation.

One great thing is that OneNote exposes its objects and methods for developers through an API for extending its features. There are many feature-rich add-ins for OneNote, which can fit your needs in several areas.

In my case, I needed a sort of 'periscope' to explore my own notes. A tool that can let me see my notes ordered by creation or last update dates, mark and retrieve some of them as favorite items, have a quick preview of a note, locate the section's file folder, search notes' titles and/or content… etc.

Using the API, I could write a 'OneNote Explorer'… a tool I started writing in 2010, enriching it with new features from time to time.

OneNote API

OneNote API is xml-based. A set of methods in the Application Interface let you get information about opened notebooks and their structures (section groups, sections, pages… etc.) in xml format.
Understanding OneNote xsd schema is thus essential.

OneNote XSD overview: main objects

 onenote xsd

  • A Notebook is (similar to file folder) a sequence of:
    • Sections
    • And/or Section groups. Where a Section group itself is a sequence of:
      • Section groups
      • And/or Sections. Where a Section is a sequence of:
        • Pages.

OneNote Page definition

onenote page xsd

  • Apart from its attributes (see xsd elements above), a Page is a set of either:
    • An Image
    • A Drawing
    • A File
    • A Media
    • An Outline (similar to html main <div>) which is (somewhat simplified here) is a sequence of:
      • OE children (OneNote Elements). Each OE can be either:
        • An Image
        • A Table
        • Drawing
        • Or a sequence of:
          • T (text range)

Application overview

Application view model classes:

 application main classes

 

A static class (OneNoteHelpers) exposes several methods to communicate with OneNote API and create / update the view model objects as required:

 code map 1

Summary of methods exposed by the helper static class:

 onenote helpers

Features

  • View selected section pages. Search titles, sort the datagrid, add page to favorites, open page in OneNote, html preview

feature 1

  • View all notebook pages. Search titles, sort the datagrid, add page to favorites, open page in OneNote, html preview

feature 2

  • Search selected notebooks

feature 3

  • Manage favorites: delete, preview, open in OneNote…

feature 4

Page preview note

The application's Preview button displays the html content of the selected page. As OneNote API can return the page xml content, an xslt style sheet (with templates per each element type of the xsd definition) allows a simple and quick preview.

sample page 

The simple page's xml tree

sample page's xsd

The page xml code

 
<?xml version="1.0" encoding="utf-8"?>
<one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2013/onenote"
         ID="{138E40BA-13BB-4D24-A78C-D92E4E23D574}{1}{E1949424590587215702781963951539196781170461}"
         name="Sample page title" dateTime="2018-04-20T18:53:38.000Z"
         lastModifiedTime="2018-04-20T19:00:30.000Z"
         pageLevel="1"
         isCurrentlyViewed="true"
         selected="partial"
         lang="en-US">
<!-- ***************** quick styles ***************************** -->
<one:QuickStyleDef index="0"
                     name="PageTitle"
                     fontColor="automatic"
                     highlightColor="automatic"
                     font="Calibri Light"
                     fontSize="20.0" spaceBefore="0.0" spaceAfter="0.0" />
 
<one:QuickStyleDef index="1"
                     name="p"
                     fontColor="automatic"
                     highlightColor="automatic"
                     font="Calibri"
                     fontSize="12.0"
                     spaceBefore="0.0" spaceAfter="0.0" />
    
<!-- ********** page settings ********** -->
<one:PageSettings RTL="false" color="automatic">
<one:PageSize>
<one:Automatic />
</one:PageSize>
<one:RuleLines visible="false" />
</one:PageSettings>
    
<!-- ********** title ********** -->
<one:Title selected="partial" lang="en-US">
<one:OE author="taoffi" authorInitials="T.N."
            lastModifiedBy="taoffi"
            lastModifiedByInitials="T.N."
            creationTime="2018-04-20T18:53:46.000Z"
            lastModifiedTime="2018-04-20T18:53:46.000Z"
            objectID="{9DB0692F-3758-49BC-8E87-F040F40599C2}{15}{B0}"
            alignment="left"
            quickStyleIndex="0" selected="partial">
<one:T><![CDATA[Sample page title]]></one:T>
<one:T selected="all"><![CDATA[]]></one:T>
</one:OE>
</one:Title>
    
<!-- ********** page content (main <div>) ********** -->
<one:Outline author="taoffi"
             authorInitials="T.N."
             lastModifiedBy="taoffi"
             lastModifiedByInitials="T.N."
             lastModifiedTime="2018-04-20T19:00:28.000Z"
             objectID="{9DB0692F-3758-49BC-8E87-F040F40599C2}{30}{B0}">
<one:Position x="36.0" y="86.4000015258789" z="0" />
<one:Size width="123.9317169189453" height="14.64842319488525" />
<one:OEChildren>
        <!-- ********** paragra^ph ********** -->
<one:OE creationTime="2018-04-20T18:53:47.000Z"
             lastModifiedTime="2018-04-20T18:53:52.000Z"
             objectID="{9DB0692F-3758-49BC-8E87-F040F40599C2}{33}{B0}"
             alignment="left"
             quickStyleIndex="1">
         <!-- ********** paragraph text ********** -->
<one:T><![CDATA[Sample page text]]></one:T>
</one:OE>
</one:OEChildren>
</one:Outline>
</one:Page>

 

OneNote 2010 vs. 2013 and above

There are some compatibility issues between OneNote API for 2010 version and 2013 and above. More changes have been introduced in 365 version.

The downloadable binaries here are for OneNote 2013 and 2016 desktop.

You may download the binaries Here!