Neatening Things Up

Well, we are pretty much on the home straight now. All that is left is a bit neatening up. Firstly we need to deal with any character styles you might be using. There is one new complication with characters styles, LibreOffice sometimes creates blank automatic ones. These don’t really hurt and could be left in, but for the sake of a couple of lines of code, we might as well remove them. If you are using any character styles, you will need to add them to the css in the root template and define how they should be rendered.


<xsl:template match="text:span">

	<xsl:variable name="style"> 
	<xsl:choose>
		<xsl:when test="contains(@text:style-name,'Novel')">
			<xsl:value-of select="@text:style-name"/>
		</xsl:when>
		<xsl:when test="@text:style-name = /office:document/office:automatic-styles/style:style[contains(@style:parent-style-name,'Novel-')]/@style:name">
			<xsl:value-of select="/office:document/office:automatic-styles/style:style[@style:name = current()/@text:style-name]/@style:parent-style-name"/>
		</xsl:when>
	</xsl:choose>
	</xsl:variable>

	<!--Sometimes LibreOffice inserts unnecessary auto character styles.
	    We want to only include intended styles -->
	<xsl:choose>
		<xsl:when test="contains($style, 'Novel-')">
			<span class="{$style}"><xsl:apply-templates /></span>
		</xsl:when>
		<xsl:otherwise>
			<xsl:apply-templates />
		</xsl:otherwise>
	</xsl:choose>
	
</xsl:template>

As you can see, as there are now three possible things in the “text:style-name” attribute (a Novel-Character style, an automatic style based on Novel-Character style and an automatic style based on nothing), we have had to add an extra <xsl:when> clause to our <xsl:choose> statement. In the third case, where this is a rouge automatic style, $style won’t contain any text and, therefore, won’t contain “Novel-”.

Any links in the document are nice and easy to deal with. It is a simple case of renaming them.


<xsl:template match="text:a">
	<a href="{@xlink:href}"><xsl:apply-templates /></a>
</xsl:template>

Images are equally easy or really tricky depending on your point of view. Assuming you only have one image, perhaps a lovely picture of the author in the author section or, in the case of the example document, a copyright graphic, you can deal with it as special case, and just put a copy of the image suitable for the kindle in an images folder manually.


<xsl:template match="draw:frame">
	<img alt="Copyleft Image" src="images/copyleft.jpg" />
</xsl:template>

If you have lots of images, that is a little beyond the scope of this plugin I’m afraid. That just leaves us with one last little bit of polish. The kindle doesn’t support drop caps (nb. This is no longer true for kindles that support KF8, old kindles it is still true for). We can get a similar effect though by making the first character of each chapter simply bigger. This is made simple by the use Novel-Paragraph-Section-First. If your layout necessitated the use of a Novel-Section-Very-First or similar additions, you just need to change the match statement to a suitable contains() one, which, seeing as you’re an old hand at this by now, will be a breeze!


<!-- Poor mans drop caps. -->
<xsl:template match="text:p[@text:style-name = 'Novel-Paragraph-Section-First' or @text:style-name = /office:document/office:automatic-styles/style:style[@style:parent-style-name = 'Novel-Paragraph-Section-First']/@style:name]">
	<p class="Novel-Pargraph-Section-First">
		<xsl:apply-templates select="node()[1]"/><xsl:apply-templates select="node()[not(position()=1)]"/>
	</p>
</xsl:template>

Here we do something a bit new, we apply templates to the first child node of of our element and all the rest of the nodes separately. Child nodes consist of the text in side the element plus any <text:span> elements in there. We do it this way as it will preserve any character styles you have in the first paragraph. We then have to have two templates to match this first node, one that matches if it is a text node and one that matches if it is span element (i.e. if the first character has a character style applied to it). We do this by checking whether the node has any text children. As a text node can’t have text children, if this node does have them, it must be a <text:span> rather than a text node. The template for the span element is a bit messy as, as in the normal span element template, we have to check whether this is real span or an unnecessary automatic span.


<xsl:template match="text:p[@text:style-name = 'Novel-Paragraph-Section-First' or @text:style-name = /office:document/office:automatic-styles/style:style[@style:parent-style-name = 'Novel-Paragraph-Section-First']/@style:name]/node()[1]">
	<span class="Novel-Char-First"><xsl:value-of select="substring(.,1,1)"/></span><xsl:value-of select="substring(., 2)"/> 
</xsl:template>

<!-- The fist character must be in span -->
<xsl:template match="text:p[@text:style-name = 'Novel-Paragraph-Section-First' or @text:style-name = /office:document/office:automatic-styles/style:style[@style:parent-style-name = 'Novel-Paragraph-Section-First']/@style:name]/node()[1][text()]">
	<xsl:variable name="style"> 
	<xsl:choose>
		<xsl:when test="contains(@text:style-name,'Novel')">
			<xsl:value-of select="@text:style-name"/>
		</xsl:when>
		<xsl:when test="@text:style-name = /office:document/office:automatic-styles/style:style[contains(@style:parent-style-name,'Novel-')]/@style:name">
			<xsl:value-of select="/office:document/office:automatic-styles/style:style[@style:name = current()/@text:style-name]/@style:parent-style-name"/>
		</xsl:when>
	</xsl:choose>
	</xsl:variable>
	<!--Sometimes LibreOffice inserts unnecessary auto character styles.
	    We want to only include intended styles -->
	<xsl:choose>
		<xsl:when test="contains($style, 'Novel-')">
			<span class="{$style}"><span class="Novel-Char-First"><xsl:value-of select="substring(text(),1,1)"/></span><xsl:value-of select="substring(text(), 2)"/></span>
		</xsl:when>
		<xsl:otherwise>
			<span class="Novel-Char-First"><xsl:value-of select="substring(text(),1,1)"/></span><xsl:value-of select="substring(text(), 2)"/>
		</xsl:otherwise>
	</xsl:choose>
</xsl:template>

And that’s it! Have a quick test of it in your browser, and if all is well, you can change the output format to “xml” and install the plugin in LibreOffice (nb. you might need to convert the exported text to Latin-1 for older kindles. We’ll discuss that and show you how to install the plugin in a second).

A Firefox window showing the structure of our document
You can now see that we have paragraphs inside chapters inside parts.
Series Navigation<< Title TemplatesInstalling the Plugin >>

Leave a Reply

Your email address will not be published. Required fields are marked *