]> granicus.if.org Git - docbook-dsssl/commitdiff
Improve both SEO and semantic structure of chunked ePub output by ensuring that
authorKeith Fahlgren <abdelazer@users.sourceforge.net>
Fri, 6 Feb 2009 04:57:22 +0000 (04:57 +0000)
committerKeith Fahlgren <abdelazer@users.sourceforge.net>
Fri, 6 Feb 2009 04:57:22 +0000 (04:57 +0000)
we always send out one and only one h1 in each XHTML chunk.

DocBook::Epub
- should include one and only one <h1> in each HTML file in rendered ePub files
for <book>s
- should include one and only one <h1> in each HTML file in rendered ePub files
for <book>s even if they do not have section markup

xsl/epub/bin/spec/epub_spec.rb
xsl/epub/docbook.xsl

index 08c8bedac63247b5fbe60fe9bb95a9d76cbc8337..8174feda0f490f06cc1254c66fbb6880f925a0da 100755 (executable)
@@ -230,6 +230,54 @@ describe DocBook::Epub do
     end
   end
 
+  it "should include one and only one <h1> in each HTML file in rendered ePub files for <book>s" do
+    begin
+      tmpdir = File.join(Dir::tmpdir(), "epubtoctest"); Dir.mkdir(tmpdir) rescue Errno::EEXIST
+      
+      epub = DocBook::Epub.new(File.join(@testdocsdir, "book.002.xml"), @tmpdir)
+      epubfile = File.join(tmpdir, "h1count.epub")
+      epub.render_to_file(epubfile, $DEBUG)
+      FileUtils.copy(epubfile, ".h1c.epub") if $DEBUG
+
+      success = system("unzip -q -d #{File.expand_path(tmpdir)} -o #{epubfile}")
+      raise "Could not unzip #{epubfile}" unless success
+      glob = Dir.glob(File.join(tmpdir, "**", "*.html"))
+      glob.each {|html_file| 
+        h1s = File.open(html_file).readlines.to_s.scan(/<h1/)
+        puts html_file if $DEBUG && h1s.length != 1 
+        h1s.length.should == 1
+      }
+    rescue => e
+      raise e
+    ensure
+      FileUtils.rm_r(tmpdir, :force => true)
+    end  
+  end
+
+  it "should include one and only one <h1> in each HTML file in rendered ePub files for <book>s even if they do not have section markup" do
+    begin
+      tmpdir = File.join(Dir::tmpdir(), "epubtoctest"); Dir.mkdir(tmpdir) rescue Errno::EEXIST
+      
+      epub = DocBook::Epub.new(File.join(@testdocsdir, "book.002.xml"), @tmpdir)
+      epubfile = File.join(tmpdir, "h1count2.epub")
+      epub.render_to_file(epubfile, $DEBUG)
+      FileUtils.copy(epubfile, ".h1c2.epub") if $DEBUG
+
+      success = system("unzip -q -d #{File.expand_path(tmpdir)} -o #{epubfile}")
+      raise "Could not unzip #{epubfile}" unless success
+      glob = Dir.glob(File.join(tmpdir, "**", "*.html"))
+      glob.each {|html_file| 
+        h1s = File.open(html_file).readlines.to_s.scan(/<h1/)
+        puts html_file if $DEBUG && h1s.length != 1 
+        h1s.length.should == 1
+      }
+    rescue => e
+      raise e
+    ensure
+      FileUtils.rm_r(tmpdir, :force => true)
+    end  
+  end
+
   it "should include a TOC link in rendered epub files for <book>s" do
     begin
       tmpdir = File.join(Dir::tmpdir(), "epubtoctest"); Dir.mkdir(tmpdir) rescue Errno::EEXIST
index 0739dadeab52bd461594759ebc0b95f6d23c597c..e20786c0d076e5efb872b19a836dcb3d1a3de761 100644 (file)
   </xsl:template>
 
 
+<!-- Change section.heading to improve SEO on generated HTML by doing heading levels 
+     "correctly". SEO rules are sometimes silly silly, but this does actually create 
+     a semantic improvement.
+     Note: This template needs to be manually maintained outside of the html/sections.xsl 
+     code, so make sure important changes get reintegrated. -->
+<xsl:template name="section.heading">
+  <xsl:param name="section" select="."/>
+  <xsl:param name="level" select="1"/>
+  <xsl:param name="allow-anchors" select="1"/>
+  <xsl:param name="title"/>
+  <xsl:param name="class" select="'title'"/>
+
+  <xsl:variable name="id">
+    <xsl:choose>
+      <!-- Make sure the subtitle doesn't get the same id as the title -->
+      <xsl:when test="self::subtitle">
+        <xsl:call-template name="object.id">
+          <xsl:with-param name="object" select="."/>
+        </xsl:call-template>
+      </xsl:when>
+      <!-- if title is in an *info wrapper, get the grandparent -->
+      <xsl:when test="contains(local-name(..), 'info')">
+        <xsl:call-template name="object.id">
+          <xsl:with-param name="object" select="../.."/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="object.id">
+          <xsl:with-param name="object" select=".."/>
+        </xsl:call-template>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <!-- For SEO, we try to actually ensure we *always* output one and only one h1,
+       so unlike the regular stylesheets, we don't add one to the section level and
+       we get the right behavior because of chunking. -->
+  <xsl:variable name="hlevel">
+    <xsl:choose>
+      <!-- highest valid HTML H level is H6; so anything nested deeper
+           than 7 levels down just becomes H6 -->
+      <xsl:when test="$level &gt; 6">6</xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$level"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:element name="h{$hlevel}" namespace="http://www.w3.org/1999/xhtml">
+    <xsl:attribute name="class"><xsl:value-of select="$class"/></xsl:attribute>
+    <xsl:if test="$css.decoration != '0'">
+      <xsl:if test="$hlevel&lt;3">
+        <xsl:attribute name="style">clear: both</xsl:attribute>
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="$allow-anchors != 0 and $generate.id.attributes = 0">
+      <xsl:call-template name="anchor">
+        <xsl:with-param name="node" select="$section"/>
+        <xsl:with-param name="conditional" select="0"/>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="$generate.id.attributes != 0 and not(local-name(.) = 'appendix')">
+      <xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>
+    </xsl:if>
+    <xsl:copy-of select="$title"/>
+  </xsl:element>
+</xsl:template>
+
+<!-- ==================================================================== -->
+
+<xsl:template match="bridgehead">
+  <xsl:variable name="container" select="(ancestor::appendix                         |ancestor::article                         |ancestor::bibliography                         |ancestor::chapter                         |ancestor::glossary                         |ancestor::glossdiv                         |ancestor::index                         |ancestor::partintro                         |ancestor::preface                         |ancestor::refsect1                         |ancestor::refsect2                         |ancestor::refsect3                         |ancestor::sect1                         |ancestor::sect2                         |ancestor::sect3                         |ancestor::sect4                         |ancestor::sect5                         |ancestor::section                         |ancestor::setindex                         |ancestor::simplesect)[last()]"/>
+
+  <xsl:variable name="clevel">
+    <xsl:choose>
+      <xsl:when test="local-name($container) = 'appendix'                       or local-name($container) = 'chapter'                       or local-name($container) = 'article'                       or local-name($container) = 'bibliography'                       or local-name($container) = 'glossary'                       or local-name($container) = 'index'                       or local-name($container) = 'partintro'                       or local-name($container) = 'preface'                       or local-name($container) = 'setindex'">1</xsl:when>
+      <xsl:when test="local-name($container) = 'glossdiv'">
+        <xsl:value-of select="count(ancestor::glossdiv)+1"/>
+      </xsl:when>
+      <xsl:when test="local-name($container) = 'sect1'                       or local-name($container) = 'sect2'                       or local-name($container) = 'sect3'                       or local-name($container) = 'sect4'                       or local-name($container) = 'sect5'                       or local-name($container) = 'refsect1'                       or local-name($container) = 'refsect2'                       or local-name($container) = 'refsect3'                       or local-name($container) = 'section'                       or local-name($container) = 'simplesect'">
+        <xsl:variable name="slevel">
+          <xsl:call-template name="section.level">
+            <xsl:with-param name="node" select="$container"/>
+          </xsl:call-template>
+        </xsl:variable>
+        <xsl:value-of select="$slevel + 1"/>
+      </xsl:when>
+      <xsl:otherwise>1</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <!-- HTML H level is one higher than section level -->
+  <xsl:variable name="hlevel">
+    <xsl:choose>
+      <xsl:when test="@renderas = 'sect1'">1</xsl:when>
+      <xsl:when test="@renderas = 'sect2'">2</xsl:when>
+      <xsl:when test="@renderas = 'sect3'">3</xsl:when>
+      <xsl:when test="@renderas = 'sect4'">4</xsl:when>
+      <xsl:when test="@renderas = 'sect5'">5</xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$clevel + 1"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:element name="h{$hlevel}" namespace="http://www.w3.org/1999/xhtml">
+    <xsl:call-template name="anchor">
+      <xsl:with-param name="conditional" select="0"/>
+    </xsl:call-template>
+    <xsl:apply-templates/>
+  </xsl:element>
+</xsl:template>
+
+<!-- SEO customization #2 -->
+<xsl:template name="component.title">
+  <xsl:param name="node" select="."/>
+
+  <xsl:variable name="level">
+    <xsl:choose>
+      <xsl:when test="ancestor::section">
+        <xsl:value-of select="count(ancestor::section)+1"/>
+      </xsl:when>
+      <xsl:when test="ancestor::sect5">6</xsl:when>
+      <xsl:when test="ancestor::sect4">5</xsl:when>
+      <xsl:when test="ancestor::sect3">4</xsl:when>
+      <xsl:when test="ancestor::sect2">3</xsl:when>
+      <xsl:when test="ancestor::sect1">2</xsl:when>
+      <xsl:otherwise>1</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:element name="h{$level}" namespace="http://www.w3.org/1999/xhtml">
+    <xsl:attribute name="class">title</xsl:attribute>
+    <xsl:if test="$generate.id.attributes = 0">
+      <xsl:call-template name="anchor">
+       <xsl:with-param name="node" select="$node"/>
+       <xsl:with-param name="conditional" select="0"/>
+      </xsl:call-template>
+    </xsl:if>
+      <xsl:apply-templates select="$node" mode="object.title.markup">
+      <xsl:with-param name="allow-anchors" select="1"/>
+    </xsl:apply-templates>
+  </xsl:element>
+</xsl:template>
+
 </xsl:stylesheet>