1 <?xml version="1.0" encoding="utf-8"?>
\r
2 <!-- Copyright 2004 by Laszlo Systems, Inc.
\r
3 Released under the Artistic License.
\r
4 Written by Oliver Steele.
\r
6 http://osteele.com/sources/xslt/htm2db/
\r
8 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
\r
9 xmlns:exslt="http://exslt.org/common"
\r
10 xmlns:java="http://xml.apache.org/xalan/java"
\r
11 xmlns:math="http://exslt.org/math"
\r
12 xmlns:db="urn:docbook"
\r
13 xmlns:h="http://www.w3.org/1999/xhtml"
\r
14 exclude-result-prefixes="exslt java math db h"
\r
17 <!-- Prefixed to every id generated from <a name=> and <a href="#"> -->
\r
18 <xsl:param name="anchor-id-prefix" select="''"/>
\r
20 <!-- Default document root; can be overridden by <?html2db class=> -->
\r
21 <xsl:param name="document-root" select="'article'"/>
\r
23 <xsl:include href="html2db-utils.xsl"/>
\r
29 <!-- pass docbook elements through unchanged; just strip the prefix
\r
31 <xsl:template match="db:*">
\r
32 <xsl:element name="{local-name()}">
\r
33 <xsl:for-each select="@*">
\r
34 <xsl:attribute name="{name()}">
\r
35 <xsl:value-of select="."/>
\r
38 <xsl:apply-templates/>
\r
42 <xsl:template match="@id">
\r
46 <!-- copy processing instructions, too -->
\r
47 <xsl:template match="processing-instruction()">
\r
51 <!-- except for html2db instructions -->
\r
52 <xsl:template match="processing-instruction('html2db')"/>
\r
54 <!-- Warn about any html elements that don't match a more
\r
55 specific template. Copy them too, since it's often
\r
56 easier to find them in the output. -->
\r
57 <xsl:template match="h:*">
\r
58 <xsl:message terminate="no">
\r
59 Unknown element <xsl:value-of select="name()"/>
\r
62 <xsl:apply-templates/>
\r
67 Root element and body
\r
70 <!-- ignore everything except the body -->
\r
71 <xsl:template match="/">
\r
72 <xsl:apply-templates select="//h:body"/>
\r
75 <xsl:template match="h:body">
\r
76 <xsl:variable name="class-pi"
\r
77 select="processing-instruction('html2db')[starts-with(string(), 'class="')][1]"/>
\r
78 <xsl:variable name="class">
\r
80 <xsl:when test="count($class-pi)!=0">
\r
81 <xsl:value-of select="substring-before(substring-after(string($class-pi[0]), 'class="'), '"')"/>
\r
84 <xsl:value-of select="$document-root"/>
\r
89 <!-- Warn if there are any text nodes outside a para, etc. See
\r
90 the note at the naked text template for why this is a
\r
92 <xsl:if test="text()[normalize-space() != '']">
\r
93 <xsl:message terminate="no">
\r
94 Text must be inside a <p> tag.
\r
98 <xsl:element name="{$class}">
\r
99 <xsl:apply-templates select="@id"/>
\r
100 <xsl:call-template name="section-content">
\r
101 <xsl:with-param name="level" select="1"/>
\r
102 <xsl:with-param name="nodes" select="//h:body/node()|//h:body/text()"/>
\r
103 </xsl:call-template>
\r
108 Section and section title processing
\r
112 Nest elements that *follow* an h1, h2, etc. into <section> elements
\r
113 such that the <h1> content is the section's <title>.
\r
115 <xsl:template name="section-content">
\r
116 <xsl:param name="level"/>
\r
117 <xsl:param name="nodes"/>
\r
118 <xsl:param name="h1" select="concat('h', $level)"/>
\r
119 <xsl:param name="h2" select="concat('h', $level+1)"/>
\r
120 <xsl:param name="h2-position" select="count(exslt:node-set($nodes)[1]/following-sibling::*[local-name()=$h2])"/>
\r
122 <!-- copy up to first h2 -->
\r
123 <xsl:apply-templates select="exslt:node-set($nodes)[
\r
124 count(following-sibling::*[local-name()=$h2])=$h2-position
\r
127 <!-- if section is empty, add an empty para so it will validate -->
\r
128 <xsl:if test="not(exslt:node-set($nodes)/h:para[
\r
129 count(following-sibling::*[local-name()=$h2])=$h2-position
\r
134 <!-- subsections -->
\r
135 <xsl:for-each select="exslt:node-set($nodes)[local-name()=$h2]">
\r
137 <xsl:variable name="mynodes" select="exslt:node-set($nodes)[
\r
138 count(following-sibling::*[local-name()=$h2])=
\r
139 count(current()/following-sibling::*[local-name()=$h2])]"/>
\r
140 <xsl:for-each select="exslt:node-set($mynodes)[local-name()=$h2]">
\r
142 <xsl:when test="@id">
\r
143 <xsl:apply-templates select="@id"/>
\r
145 <xsl:when test="h:a/@name">
\r
146 <xsl:attribute name="id">
\r
147 <xsl:value-of select="concat($anchor-id-prefix, h:a/@name)"/>
\r
152 <xsl:call-template name="section-content">
\r
153 <xsl:with-param name="level" select="$level+1"/>
\r
154 <xsl:with-param name="nodes" select="exslt:node-set($nodes)[
\r
155 count(following-sibling::*[local-name()=$h2])=
\r
156 count(current()/following-sibling::*[local-name()=$h2])]"/>
\r
157 </xsl:call-template>
\r
163 Remove anchors from hn titles. section-content attaches these as ids
\r
164 to the section (after mutilating them as described in the docs).
\r
166 <xsl:template match="h:h1|h:h2|h:h3|h:h4|h:h5|h:h6">
\r
168 <xsl:apply-templates mode="skip-anchors" select="node()"/>
\r
172 <xsl:template mode="skip-anchors" match="h:a[@name]">
\r
173 <xsl:apply-templates/>
\r
176 <xsl:template mode="skip-anchors" match="node()">
\r
177 <xsl:apply-templates select="."/>
\r
183 <xsl:template match="h:b|h:i|h:em|h:strong">
\r
184 <emphasis role="{local-name()}">
\r
185 <xsl:apply-templates/>
\r
189 <xsl:template match="h:dfn">
\r
190 <indexterm significance="preferred">
\r
191 <primary><xsl:apply-templates/></primary>
\r
193 <glossterm><xsl:apply-templates/></glossterm>
\r
196 <xsl:template match="h:var">
\r
197 <replaceable><xsl:apply-templates/></replaceable>
\r
201 Inline elements in code
\r
203 <xsl:template match="h:code/h:i|h:tt/h:i|h:pre/h:i">
\r
205 <xsl:apply-templates/>
\r
209 <xsl:template match="h:code|h:tt">
\r
211 <xsl:if test="@class">
\r
212 <xsl:attribute name="role"><xsl:value-of select="@class"/></xsl:attribute>
\r
214 <xsl:apply-templates/>
\r
218 <!-- For now, everything that doesn't have a specific match in inline
\r
219 processing mode is matched against the default processing mode. -->
\r
220 <xsl:template mode="inline" match="*">
\r
221 <xsl:apply-templates select="."/>
\r
227 <xsl:template match="h:p">
\r
229 <xsl:apply-templates select="@id"/>
\r
230 <xsl:apply-templates mode="inline"/>
\r
234 <!-- Wrap naked text nodes in a <para> so that they'll process more
\r
235 correctly. The h:body also warns about these, because even
\r
236 this preprocessing step isn't guaranteed to fix them. This is
\r
237 because "Some <i>italic</i> text" will be preprocessed into
\r
238 "<para>Some </para> <emphasis>italic</emphasis><para>
\r
239 text</para>" instead of "<para>Some <emphasis>italic</emphasis>
\r
240 text</para>". Getting this right would require more work than
\r
241 just maintaining the source documents. -->
\r
242 <xsl:template match="h:body/text()[normalize-space()!= '']">
\r
243 <!-- add an invalid tag to make it easy to find this in
\r
244 the generated file -->
\r
247 <xsl:apply-templates/>
\r
252 <xsl:template match="h:body/h:code|h:pre">
\r
254 <xsl:apply-templates/>
\r
258 <xsl:template match="h:blockquote">
\r
260 <xsl:apply-templates mode="item" select="."/>
\r
267 <xsl:template name="imageobject">
\r
269 <imagedata fileref="{@src}">
\r
270 <xsl:apply-templates select="@width|@height"/>
\r
275 <xsl:template match="h:img/@width">
\r
279 <xsl:template match="h:img">
\r
280 <xsl:param name="informal">
\r
281 <xsl:if test="not(@title) and not(db:title)">informal</xsl:if>
\r
283 <xsl:element name="{$informal}figure">
\r
284 <xsl:apply-templates select="@id"/>
\r
286 <xsl:when test="@title">
\r
287 <title><xsl:value-of select="@title"/></title>
\r
290 <xsl:apply-templates select="db:title"/>
\r
294 <xsl:call-template name="imageobject"/>
\r
295 <xsl:if test="@alt and normalize-space(@alt)!=''">
\r
298 <xsl:value-of select="@alt"/>
\r
306 <xsl:template mode="inline" match="h:img">
\r
307 <inlinemediaobject>
\r
308 <xsl:apply-templates select="@id"/>
\r
309 <xsl:call-template name="imageobject"/>
\r
310 </inlinemediaobject>
\r
318 <xsl:template match="h:a[@name]">
\r
319 <anchor id="{$anchor-id-prefix}{@name}"/>
\r
320 <xsl:apply-templates/>
\r
323 <!-- internal link -->
\r
324 <xsl:template match="h:a[starts-with(@href, '#')]">
\r
325 <link linkend="{$anchor-id-prefix}{substring-after(@href, '#')}">
\r
326 <xsl:apply-templates select="@*"/>
\r
327 <xsl:apply-templates/>
\r
331 <!-- external link -->
\r
332 <xsl:template match="h:a">
\r
333 <ulink url="{@href}">
\r
334 <xsl:apply-templates select="@*"/>
\r
335 <xsl:apply-templates/>
\r
340 <xsl:template match="h:a[starts-with(@href, 'mailto:')]">
\r
342 <xsl:apply-templates select="@*"/>
\r
343 <xsl:value-of select="substring-after(@href, 'mailto:')"/>
\r
347 <!-- link attributes -->
\r
349 <xsl:template match="h:a/@*"/>
\r
351 <xsl:template match="h:a/@id">
\r
352 <xsl:apply-templates select="@id"/>
\r
355 <xsl:template match="h:a/@target|h:a/@link">
\r
356 <xsl:processing-instruction name="db2html">
\r
357 <xsl:text>attribute name="</xsl:text>
\r
358 <xsl:value-of select="name()"/>
\r
359 <xsl:text>" value=</xsl:text>
\r
360 <xsl:call-template name="quote"/>
\r
361 </xsl:processing-instruction>
\r
368 <xsl:template match="h:dl">
\r
370 <xsl:apply-templates select="db:*"/>
\r
371 <xsl:apply-templates select="h:dt"/>
\r
375 <xsl:template match="h:dt">
\r
376 <xsl:variable name="item-number" select="count(preceding-sibling::h:dt)+1"/>
\r
379 <xsl:apply-templates/>
\r
382 <!-- Select the dd that follows this dt without an intervening dd -->
\r
383 <xsl:apply-templates mode="item"
\r
384 select="following-sibling::h:dd[
\r
385 count(preceding-sibling::h:dt)=$item-number
\r
387 <!-- If there is no such dd, then insert an empty para -->
\r
388 <xsl:if test="count(following-sibling::h:dd[
\r
389 count(preceding-sibling::h:dt)=$item-number
\r
397 <xsl:template mode="item" match="*[count(h:p) = 0]">
\r
399 <xsl:apply-templates/>
\r
403 <xsl:template mode="nonblank-nodes" match="node()">
\r
404 <xsl:element name="{local-name()}"/>
\r
407 <xsl:template mode="nonblank-nodes" match="text()[normalize-space()='']"/>
\r
409 <xsl:template mode="nonblank-nodes" match="text()">
\r
413 <xsl:template mode="item" match="*">
\r
414 <!-- Test whether the first non-blank node is not a p -->
\r
415 <xsl:param name="nonblank-nodes">
\r
416 <xsl:apply-templates mode="nonblank-nodes"/>
\r
419 <xsl:param name="tested" select="
\r
420 count(exslt:node-set($nonblank-nodes)/*) != 0 and
\r
421 local-name(exslt:node-set($nonblank-nodes)/*[1]) != 'p'"/>
\r
423 <xsl:param name="n1" select="count(*[1]/following::h:p)"/>
\r
424 <xsl:param name="n2" select="count(text()[1]/following::h:p)"/>
\r
426 <xsl:param name="n">
\r
427 <xsl:if test="$tested">
\r
428 <xsl:value-of select="java:java.lang.Math.max($n1, $n2)"/>
\r
432 <xsl:if test="false()">
\r
433 <nodeset tested="{$tested}" count="{count(exslt:node-set($nonblank-nodes)/*)}">
\r
434 <xsl:for-each select="exslt:node-set($nonblank-nodes)/*">
\r
435 <element name="{local-name()}"/>
\r
440 <!-- Wrap everything before the first p into a para -->
\r
441 <xsl:if test="$tested">
\r
443 <xsl:apply-templates select="
\r
444 node()[count(following::h:p)=$n] |
\r
445 text()[count(following::h:p)=$n]"/>
\r
448 <xsl:apply-templates select="
\r
449 node()[count(following::h:p)!=$n] |
\r
450 text()[count(following::h:p)!=$n]"/>
\r
453 <xsl:template match="h:ol">
\r
454 <orderedlist spacing="compact">
\r
455 <xsl:for-each select="h:li">
\r
457 <xsl:apply-templates mode="item" select="."/>
\r
463 <xsl:template match="h:ul">
\r
464 <itemizedlist spacing="compact">
\r
465 <xsl:for-each select="h:li">
\r
467 <xsl:apply-templates mode="item" select="."/>
\r
473 <xsl:template match="h:ul[processing-instruction('html2db')]">
\r
475 <xsl:for-each select="h:li">
\r
476 <member type="vert">
\r
477 <xsl:apply-templates mode="item" select="."/>
\r
486 <xsl:template match="h:br">
\r
487 <xsl:processing-instruction name="db2html">
\r
488 <xsl:text>element="</xsl:text>
\r
489 <xsl:value-of select="local-name()"/>
\r
490 <xsl:text>"</xsl:text>
\r
491 </xsl:processing-instruction>
\r
494 <xsl:template match="h:span|h:div">
\r
495 <xsl:apply-templates select="*|node()|text()"/>
\r
499 Utility functions and templates for tables
\r
501 <xsl:template mode="count-columns" match="h:tr">
\r
503 <xsl:value-of select="count(h:td)"/>
\r
508 <xsl:template match="h:table">
\r
509 <xsl:param name="informal">
\r
510 <xsl:if test="not(@summary)">informal</xsl:if>
\r
512 <xsl:param name="colcounts">
\r
513 <xsl:apply-templates mode="count-columns" select=".//h:tr"/>
\r
515 <xsl:param name="cols" select="math:max(exslt:node-set($colcounts)/n)"/>
\r
516 <xsl:param name="sorted">
\r
517 <xsl:for-each select="exslt:node-set($colcounts)/n">
\r
518 <xsl:sort order="descending" data-type="number"/>
\r
519 <n><xsl:value-of select="."/></n>
\r
522 <xsl:element name="{$informal}table">
\r
523 <xsl:apply-templates select="@id"/>
\r
524 <xsl:if test="processing-instruction('html2db')[starts-with(., 'rowsep')]">
\r
525 <xsl:attribute name="rowsep">1</xsl:attribute>
\r
527 <xsl:apply-templates select="processing-instruction()"/>
\r
528 <xsl:if test="@summary">
\r
529 <title><xsl:value-of select="@summary"/></title>
\r
531 <tgroup cols="{$cols}">
\r
532 <xsl:if test=".//h:tr/h:th">
\r
534 <xsl:for-each select=".//h:tr[count(h:th)!=0]">
\r
536 <xsl:apply-templates select="@id"/>
\r
537 <xsl:for-each select="h:td|h:th">
\r
539 <xsl:apply-templates select="@id"/>
\r
540 <xsl:apply-templates/>
\r
544 <xsl:text> </xsl:text> <!-- cr -->
\r
549 <xsl:for-each select=".//h:tr[count(h:th)=0]">
\r
551 <xsl:apply-templates select="@id"/>
\r
552 <xsl:for-each select="h:td|h:th">
\r
554 <xsl:apply-templates select="@id"/>
\r
555 <xsl:apply-templates/>
\r
559 <xsl:text> </xsl:text> <!-- cr -->
\r