Transforming plain text to SVG for EPUB 3

The guiding principle of the SEED.html app's data handling is that plain text source content, stored inside the EPUB file, is transformed at authoring time to its xhtml representation.

The previous blog post provides an overview of how this works for chapter-level content for various formats including markdown, textile and org mode.

This post looks at other plain text formats that can be transformed into SVG and included as inline images within the chapter content.

Code blocks and DOM transforms

The SEED.html app provides a text processing pipeline hook called that will be called after the initial transformation of plain text and before the chapter xhtml is written to a file.

The javascript function signature looks like this; function transformDOM(document, idref)

The first argument is the dom that results from the plain text transform. The second argument is the manifest item idref for the current chapter. Providing the idref argument allows dom processing to be different per chapter.

This means that if the plain text format provides a way of delimiting a block of plain text (like a blockquote or a pre code block) the transformDom operation can further process that text into some other kind of content, for instance an SVG image.

An example makes this clear.

ABC notation

Here's a block of markdown that includes a code block delimited by triple back-ticks, with a language type of abc specified after the opening delimiter.

## Simple scale

```abc
L:1/4
K:C
V: 1 treble-8
CDEF|GABc |]
w: do re mi fa so la ti do
```

You can read all about abc notation at abcnotation.com. I won't cover it further here.

The markdown processing results in a dom which includes the equivalent of this html fragment;

<h2>Simple scale</h2>
<pre>
  <code class="language-abc">
L:1/4
K:C
V: 1 treble-8
CDEF|GABc |]
w: do re mi fa so la ti do
  </code>
</pre>

This is the document argument that gets passed into transformDom(document, idref).

So then a suitable implementation of transformDom() might pull the text content from that code block and do something with it, like pass it to the abc2svg renderer like this;

document.querySelectorAll('.language-abc').forEach(el => {
  const renderOptions = { /* removed for brevity */ };
  const abc = new abc2svg.Abc(renderOptions);
  abc.tosvg("A song", el.textContent);
});

There's a bunch of additional code not shown that is needed to set up the abc rendering using the abc2svg library, but in summary it's possible to swap the pre code element out of the dom and replace it with the rendered svg image. Here's what the resulting chapter xhtml looks like with the inline SVG element.

<h2>Simple scale</h2>
<div>
   <svg xmlns="http://www.w3.org/2000/svg" version="1.1"
        xmlns:xlink="http://www.w3.org/1999/xlink"
        fill="currentColor" stroke-width=".7" 
        class="f102 tune0" viewBox="0 0 794 102">
      <g class="g" transform="scale(1.33)">
         <g transform="translate(0.0, 47.0)">
            <path class="slW" d="m0 0h231.5m-231.5 -6h231.5m-231.5 -6h231.5m-231.5 -6h231.5m-231.5 -6h231.5"/>
         </g>
         <path class="stroke" stroke-width="1" d="M32.7 53.0m-6.11 0h12.22"/>
         <path class="bW" d="M127.1 47.0v-24.0M225.5 47.0v-24.0"/>
         <path class="bthW" d="M230.0 47.0v-24.0"/>
         <text x="4.0,10.0,29.0,53.7,78.4,103.1,135.1,159.8,184.5,209.2"
           y="41.0,63.0,53.0,50.0,47.0,44.0,41.0,38.0,35.0,32.0"></text>
         <path class="sW" d="M36.2 53.0v-21.0M60.9 50.0v-21.0M85.6 47.0v-21.0M110.3 44.0v-21.0M142.3 41.0v-21.0M167.0 38.0v-21.0M191.7 35.0v-21.0M209.4 32.0v21.0"/>
         <g transform="translate(0,47.0)">
            <text class="f99" x="26.3" y="24.1">do</text>
            <text class="f99" x="52.4" y="24.1">re</text>
            <text class="f99" x="75.3" y="24.1">mi</text>
            <text class="f99" x="101.8" y="24.1">fa</text>
            <text class="f99" x="133.1" y="24.1">so</text>
            <text class="f99" x="158.9" y="24.1">la</text>
            <text class="f99" x="184.6" y="24.1">ti</text>
            <text class="f99" x="206.5" y="24.1">do</text>
         </g>
      </g>
   </svg>
</div>

When rendered in an EPUB reading system that looks like this (but is an SVG not a bitmap image);

End Result

This leaves us with plain text chapter content in markdown format with plain text musical notation in abc format that renders to high-quality SVG in an EPUB book.

Further reading

A previous post includes an EPUB preview which explains abc notation a bit more.

Another post describes a refinement of this approach to enable multiple renderings with the aim of being responsive to device configuration.

Previous Post
No Comment
Add Comment
comment url