Шукати в цьому блозі

вівторок, 21 жовтня 2014 р.

FB2 читалка для Linux-консоли, fb2 to txt, fb2 to xhtml, fb2 to html

Собственно всё сводится к простому xsl-преобразованию. Для начала рисуем fb2_2_txt.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:fb="http://www.gribuser.ru/xml/fictionbook/2.0">
        <xsl:strip-space elements="*"/>
        <xsl:output method="text" encoding="UTF-8"/>
        <xsl:key name="note-link" match="fb:section|fb:p" use="@id"/>

        <xsl:template match="*">
<!-- <xsl:variable/> -->
<xsl:value-of select="fb:description/fb:title-info/fb:book-title"/>
<xsl:text >
</xsl:text>
<xsl:for-each select="fb:description/fb:title-info/fb:author">    <xsl:call-template name="author"/>
<xsl:text >
</xsl:text>
</xsl:for-each>
<xsl:text >
</xsl:text>

<xsl:if test="fb:description/fb:title-info/fb:sequence">
<xsl:for-each select="fb:description/fb:title-info/fb:sequence"><xsl:call-template name="sequence"/></xsl:for-each>
<xsl:text >
</xsl:text>
</xsl:if>
<xsl:for-each select="fb:description/fb:title-info/fb:annotation">
<xsl:call-template name="annotation"/>
<xsl:text >
</xsl:text>
</xsl:for-each>
                                <!-- BUILD BOOK -->
<xsl:for-each select="fb:body">
<xsl:if test="position()!=1">
<xsl:text >

</xsl:text>
</xsl:if>

<xsl:if test="@name">
<xsl:value-of select="@name"/>
<xsl:text >
</xsl:text>
</xsl:if>
<!-- <xsl:apply-templates /> -->
<xsl:apply-templates/>
</xsl:for-each>
<xsl:text >
</xsl:text>
        </xsl:template>
        <!-- author template -->
<xsl:template name="author">
        <xsl:value-of select="fb:first-name"/>
        <xsl:text> </xsl:text>
        <xsl:if test="fb:middle-name and fb:middle-name!=''">
                <xsl:value-of select="fb:middle-name" />
                <xsl:text> </xsl:text>
        </xsl:if> 
        <xsl:value-of select="fb:last-name"/>
        <xsl:text >
</xsl:text>
</xsl:template>
        <!-- secuence template -->
<xsl:template name="sequence">
<xsl:value-of select="@name"/><xsl:if test="@number"><xsl:text > #</xsl:text><xsl:value-of select="@number"/></xsl:if>
<xsl:if test="fb:sequence">
        <xsl:for-each select="fb:sequence">
<xsl:call-template name="sequence"/>
</xsl:for-each>
</xsl:if></xsl:template>

<!-- description -->
<xsl:template match="fb:description">
<xsl:apply-templates/>
<xsl:text >
</xsl:text>
</xsl:template>
<!-- body -->
<xsl:template match="fb:body">
<xsl:text >
</xsl:text>
<xsl:apply-templates/>
</xsl:template>

<xsl:template match="fb:section">
<xsl:text >
</xsl:text>
<xsl:apply-templates select="./*"/>
</xsl:template>


<!-- section/title -->
<xsl:template match="fb:title">
<xsl:text >

</xsl:text>
<xsl:apply-templates/>
<xsl:text >
</xsl:text>
</xsl:template>

        <!-- subtitle -->
<xsl:template match="fb:subtitle">
<xsl:text >
</xsl:text>
<xsl:apply-templates/>
<xsl:text >

</xsl:text>
</xsl:template>

<!-- p -->
<xsl:template match="fb:p">
<xsl:apply-templates/>
<xsl:text >

</xsl:text>
</xsl:template>

<xsl:template match="fb:p" mode="note">
<xsl:apply-templates/>
</xsl:template>

<xsl:template match="fb:title" mode="note">
<xsl:apply-templates mode="note"/><xsl:text disable-output-escaping="yes"> - </xsl:text>
</xsl:template>


<xsl:template match="fb:strong|fb:emphasis|fb:style"><xsl:apply-templates/></xsl:template>

<xsl:template match="fb:a">
<xsl:choose>
<xsl:when test="(@type) = 'note'">
<xsl:choose>
<xsl:when test="starts-with(@xlink:href,'#')"><xsl:for-each select="key('note-link',substring-after(@xlink:href,'#'))">[<xsl:apply-templates mode="note"/>]</xsl:for-each></xsl:when>
<xsl:otherwise><xsl:for-each select="key('note-link',@xlink:href)">[<xsl:apply-templates mode="note"/>]</xsl:for-each></xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template match="fb:empty-line">
<xsl:text >

</xsl:text>
</xsl:template>

<!-- annotation -->
<xsl:template name="annotation">
<xsl:apply-templates/>
<xsl:text >
</xsl:text>
</xsl:template>

<!-- epigraph -->
<xsl:template match="fb:epigraph">
<xsl:apply-templates/>
<xsl:text >
</xsl:text>
</xsl:template>

<!-- cite -->
<xsl:template match="fb:cite">
        <xsl:text >

</xsl:text>
        <xsl:apply-templates/>
        <xsl:text >
</xsl:text>
</xsl:template>


        <!-- cite/text-author -->
<xsl:template match="fb:text-author">
        <xsl:text >    </xsl:text>
        <xsl:apply-templates/>
        <xsl:text >
</xsl:text>
</xsl:template>
        <!-- date -->
<xsl:template match="fb:date">
        <xsl:text >    </xsl:text>
        <xsl:apply-templates/>
        <xsl:text >
</xsl:text>
</xsl:template>

<xsl:template match="fb:poem">
<xsl:apply-templates/>
</xsl:template>

        <!-- stanza -->
<xsl:template match="fb:stanza">
<xsl:apply-templates/>
<xsl:text >
</xsl:text>
</xsl:template>
        <!-- v -->
<xsl:template match="fb:v">
<xsl:text >               </xsl:text>
<xsl:apply-templates/>
<xsl:text >
</xsl:text>
</xsl:template>

</xsl:stylesheet>


Затем, в консоли, выполняем:
zcat book.fb2.zip | xsltproc fb2_2_txt.xsl - | less -s

Более того, подобное преобразование можно выполнить для преобразования fb2 в xhtml, с сохранением картинок (спасибо tkf http://habrahabr.ru/post/75827/), файл fb2_22_xhtml.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:fb="http://www.gribuser.ru/xml/fictionbook/2.0">
        <xsl:output method="xml" encoding="UTF-8"/>
        <xsl:key name="note-link" match="fb:section" use="@id"/>
        <xsl:template match="/*">
                <html>
                        <head>
                                <xsl:if test="fb:description/fb:title-info/fb:lang = 'ru'">
                                        <meta HTTP-EQUIV="content-type" CONTENT="text/html; charset=UTF-8"/>
                                </xsl:if>
                                <title>
                                        <xsl:value-of select="fb:description/fb:title-info/fb:book-title"/>
                                </title>
                                <style type="text/x-oeb1-css">
                                        A { color : #0002CC }
                                        A:HOVER { color : #BF0000 }
                                        BODY { background-color : #FEFEFE; color : #000000; font-family : Verdana, Geneva, Arial, Helvetica, sans-serif; text-align : justify }
                                        H1{ font-size : 160%; font-style : normal; font-weight : bold; text-align : left; border : 1px solid Black;  background-color : #E7E7E7; margin-left : 0px;  page-break-before : always; }
                                        H2{ font-size : 130%; font-style : normal; font-weight : bold; text-align : left; background-color : #EEEEEE;  border : 1px solid Gray;  page-break-before : always; }
                                        H3{ font-size : 110%; font-style : normal; font-weight : bold; text-align : left;  background-color : #F1F1F1;  border : 1px solid Silver;}
                                        H4{ font-size : 100%; font-style : normal; font-weight : bold; text-align : left; border : 1px solid Gray;  background-color : #F4F4F4;}
                                        H5{ font-size : 100%; font-style : italic; font-weight : bold; text-align : left; border : 1px solid Gray;  background-color : #F4F4F4;}
                                        H6{ font-size : 100%; font-style : italic; font-weight : normal; text-align : left; border : 1px solid Gray;  background-color : #F4F4F4;}
                                        SMALL{ font-size : 80% }
                                        BLOCKQUOTE{ margin-left :4em; margin-top:1em; margin-right:0.2em;}
                                        HR{ color : Black }
                                        UL{margin-left: 0}
                                        .epigraph{width:50%; margin-left : 35%;}
                                </style>
                        </head>
                        <body>


                                                <h4 align="center">
                                                <xsl:value-of select="fb:description/fb:title-info/fb:book-title"/>
                                                </h4>

<xsl:for-each select="fb:description/fb:title-info/fb:coverpage/fb:image">
                <xsl:call-template name="image"/>
                </xsl:for-each>

                                <xsl:for-each select="fb:description/fb:title-info/fb:annotation">

                                        <div>
                                                <xsl:call-template name="annotation"/>
                                        </div>
                                        <hr/>
                                </xsl:for-each>
                                <!-- BUILD TOC -->
                                <ul>
                                        <xsl:apply-templates select="fb:body" mode="toc"/>
                                </ul>
                                <hr/>
                                <!-- BUILD BOOK -->
                                <xsl:for-each select="fb:body">
                                        <xsl:if test="position()!=1">
                                                <hr/>
                                        </xsl:if>
                                        <xsl:if test="@name">
                                                <h4 align="center">
                                                        <xsl:value-of select="@name"/>
                                                </h4>
                                        </xsl:if>
                                        <!-- <xsl:apply-templates /> -->
                                        <xsl:apply-templates/>
                                </xsl:for-each>
                        </body>
                </html>
        </xsl:template>
        <!-- author template -->
        <xsl:template name="author">
                <xsl:value-of select="fb:first-name"/>
                <xsl:text disable-output-escaping="no"> </xsl:text>
                <xsl:value-of select="fb:middle-name"/> 
         <xsl:text disable-output-escaping="no"> </xsl:text>
                <xsl:value-of select="fb:last-name"/>
                <br/>
        </xsl:template>
        <!-- secuence template -->
        <xsl:template name="sequence">
                <LI/>
                <xsl:value-of select="@name"/>
                <xsl:if test="@number">
                        <xsl:text disable-output-escaping="no">, #</xsl:text>
                        <xsl:value-of select="@number"/>
                </xsl:if>
                <xsl:if test="fb:sequence">
                        <ul>
                                <xsl:for-each select="fb:sequence">
                                        <xsl:call-template name="sequence"/>
                                </xsl:for-each>
                        </ul>
                </xsl:if>
                <!--      <br/> -->
        </xsl:template>
        <!-- toc template -->
        <xsl:template match="fb:section|fb:body" mode="toc">
                <xsl:choose>
                        <xsl:when test="name()='body' and position()=1 and not(fb:title)">
                                <xsl:apply-templates select="fb:section" mode="toc"/>
                        </xsl:when>
                        <xsl:otherwise>
                                <li>
                                        <a href="#TOC_{generate-id()}"><xsl:value-of select="normalize-space(fb:title/fb:p[1] | @name)"/></a>
                                        <xsl:if test="fb:section">
                                                <ul><xsl:apply-templates select="fb:section" mode="toc"/></ul>
                                        </xsl:if>
                                </li>
                        </xsl:otherwise>
                </xsl:choose>
        </xsl:template>
        <!-- description -->
        <xsl:template match="fb:description">
                <xsl:apply-templates/>
        </xsl:template>
        <!-- body -->
        <xsl:template match="fb:body">
                <div><xsl:apply-templates/></div>
        </xsl:template>

        <xsl:template match="fb:section">
                <a name="TOC_{generate-id()}"></a>
                <xsl:if test="@id">
                        <xsl:element name="a">
                                <xsl:attribute name="name"><xsl:value-of select="@id"/></xsl:attribute>
                        </xsl:element>
                </xsl:if>
                <xsl:apply-templates/>
        </xsl:template>


        <!-- section/title -->
        <xsl:template match="fb:section/fb:title|fb:poem/fb:title">
                <xsl:choose>
                        <xsl:when test="count(ancestor::node()) < 9">
                                <xsl:element name="{concat('h',count(ancestor::node())-3)}">
                                        <a name="TOC_{generate-id()}"></a>
                                        <xsl:if test="@id">
                                                <xsl:element name="a">
                                                        <xsl:attribute name="name"><xsl:value-of select="@id"/></xsl:attribute>
                                                </xsl:element>
                                        </xsl:if>
                                        <xsl:apply-templates/>
                                </xsl:element>
                        </xsl:when>
                        <xsl:otherwise>
                                <xsl:element name="h6">
                                        <xsl:if test="@id">
                                                <xsl:element name="a">
                                                        <xsl:attribute name="name"><xsl:value-of select="@id"/></xsl:attribute>
                                                </xsl:element>
                                        </xsl:if>
                                        <xsl:apply-templates/>
                                </xsl:element>
                        </xsl:otherwise>
                </xsl:choose>
        </xsl:template>
        <!-- section/title -->
        <xsl:template match="fb:body/fb:title">
                <h1><xsl:apply-templates mode="title"/></h1>
        </xsl:template>

        <xsl:template match="fb:title/fb:p">
                <xsl:apply-templates/><xsl:text disable-output-escaping="no"> </xsl:text><br/>
        </xsl:template>
        <!-- subtitle -->
        <xsl:template match="fb:subtitle">
                <xsl:if test="@id">
                        <xsl:element name="a">
                                <xsl:attribute name="name"><xsl:value-of select="@id"/></xsl:attribute>
                        </xsl:element>
                </xsl:if>
                <h5>
                        <xsl:apply-templates/>
                </h5>
        </xsl:template>
        <!-- p -->
        <xsl:template match="fb:p">
                <div align="justify"><xsl:if test="@id">
                                <xsl:element name="a">
                                        <xsl:attribute name="name"><xsl:value-of select="@id"/></xsl:attribute>
                                </xsl:element>
                        </xsl:if>    <xsl:apply-templates/></div>
        </xsl:template>
        <!-- strong -->
        <xsl:template match="fb:strong">
                <b><xsl:apply-templates/></b>
        </xsl:template>
        <!-- emphasis -->
        <xsl:template match="fb:emphasis">
                <i>       <xsl:apply-templates/></i>
        </xsl:template>
        <!-- style -->
        <xsl:template match="fb:style">
                <span class="{@name}"><xsl:apply-templates/></span>
        </xsl:template>
        <!-- empty-line -->
        <xsl:template match="fb:empty-line">
                <br/>
        </xsl:template>
        <!-- link -->
        <xsl:template match="fb:a">
                <xsl:element name="a">
                        <xsl:attribute name="href"><xsl:value-of select="@xlink:href"/></xsl:attribute>
                        <xsl:attribute name="title">
                                <xsl:choose>
                                        <xsl:when test="starts-with(@xlink:href,'#')"><xsl:value-of select="key('note-link',substring-after(@xlink:href,'#'))/fb:p"/></xsl:when>
                                        <xsl:otherwise><xsl:value-of select="key('note-link',@xlink:href)/fb:p"/></xsl:otherwise>
                                </xsl:choose>
                        </xsl:attribute>
                        <xsl:choose>
                                <xsl:when test="(@type) = 'note'">
                                        <sup>
                                                <xsl:apply-templates/>
                                        </sup>
                                </xsl:when>
                                <xsl:otherwise>
                                        <xsl:apply-templates/>
                                </xsl:otherwise>
                        </xsl:choose>
                </xsl:element>
        </xsl:template>
        <!-- annotation -->
        <xsl:template name="annotation">
                <xsl:if test="@id">
                        <xsl:element name="a">
                                <xsl:attribute name="name"><xsl:value-of select="@id"/></xsl:attribute>
                        </xsl:element>
                </xsl:if>
                <h3>Annotation</h3>
                <xsl:apply-templates/>
        </xsl:template>
        <!-- epigraph -->
        <xsl:template match="fb:epigraph">
                <blockquote class="epigraph">
                        <xsl:if test="@id">
                                <xsl:element name="a">
                                        <xsl:attribute name="name"><xsl:value-of select="@id"/></xsl:attribute>
                                </xsl:element>
                        </xsl:if>
                        <xsl:apply-templates/>
                </blockquote>
        </xsl:template>
        <!-- epigraph/text-author -->
        <xsl:template match="fb:epigraph/fb:text-author">
                <blockquote>
                        <i><xsl:apply-templates/></i>
                </blockquote>
        </xsl:template>
        <!-- cite -->
        <xsl:template match="fb:cite">
                <blockquote>
                <xsl:if test="@id">
                        <xsl:element name="a">
                                <xsl:attribute name="name"><xsl:value-of select="@id"/></xsl:attribute>
                        </xsl:element>
                </xsl:if>
                <xsl:apply-templates/>
                </blockquote>
        </xsl:template>
        <!-- cite/text-author -->
        <xsl:template match="fb:text-author">
                <blockquote>
                <i>       <xsl:apply-templates/></i></blockquote>
        </xsl:template>
        <!-- date -->
        <xsl:template match="fb:date">
                <xsl:choose>
                        <xsl:when test="not(@value)">
                                   <xsl:apply-templates/>
                                <br/>
                        </xsl:when>
                        <xsl:otherwise>
                                   <xsl:value-of select="@value"/>
                                <br/>
                        </xsl:otherwise>
                </xsl:choose>
        </xsl:template>
        <!-- poem -->
        <xsl:template match="fb:poem">
                <blockquote>
                        <xsl:if test="@id">
                                <xsl:element name="a">
                                        <xsl:attribute name="name"><xsl:value-of select="@id"/></xsl:attribute>
                                </xsl:element>
                        </xsl:if>
                        <xsl:apply-templates/>
                </blockquote>
        </xsl:template>

        <!-- stanza -->
        <xsl:template match="fb:stanza">
                <xsl:apply-templates/>
                <br/>
        </xsl:template>
        <!-- v -->
        <xsl:template match="fb:v">
                <xsl:if test="@id">
                        <xsl:element name="a">
                                <xsl:attribute name="name"><xsl:value-of select="@id"/></xsl:attribute>
                        </xsl:element>
                </xsl:if>
                <xsl:apply-templates/><br/>
        </xsl:template>
        <!-- image -->
        <xsl:template match="fb:image" name="image">
                <div align="center">
                        <img border="1">
                                <xsl:choose>
                                        <xsl:when test="starts-with(@xlink:href,'#')">
                                                <xsl:attribute name="src"><xsl:text>data:</xsl:text><xsl:variable name="href" select="substring-after(@xlink:href,'#')" /><set variable="href" expression="substring-after(@xlink:href,'#')"/><xsl:value-of select="//fb:binary[@id=$href]/@content-type" disable-output-escaping="yes" /><xsl:text>;base64,</xsl:text><!--<xsl:value-of select="substring-after(@xlink:href,'#')"/>--><xsl:value-of select="//fb:binary[@id=$href]"  disable-output-escaping="yes"/></xsl:attribute>

                                        </xsl:when>
                                        <xsl:otherwise>
                                                <xsl:attribute name="src"><xsl:value-of select="@xlink:href"/></xsl:attribute>
                                        </xsl:otherwise>
                                </xsl:choose>
                        </img>
                </div>
        </xsl:template>
</xsl:stylesheet>

Далее, например, так:
zcat book.fb2.zip | xsltproc fb2_22_xhtml.xsl - | lynx -force_html -stdin -dump -nolist | less

Немає коментарів: