dbtoepub 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/dbtoepub +76 -0
- data/lib/docbook.rb +227 -0
- metadata +46 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: d62171d3ad82dff798473353b3b55322c6ce7afb
         | 
| 4 | 
            +
              data.tar.gz: 7bb725b3ea583d42bfcdede5957546dc4591eeec
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: f28ea47865935fcd124d13b4e54d50eedbe9a53480626792dec14803afcb073a273cfd386b6a277cbd1138117b686c0936b4441068aca88f96be68eefede5535
         | 
| 7 | 
            +
              data.tar.gz: 555e7668a6f87edae840b9ce5868dfff8b351a760eb9b06bb211486dfb7a1995f8250767356377a780015460f76acdb9ebcf7fb167a6cd65034ab58c55f1479d
         | 
    
        data/bin/dbtoepub
    ADDED
    
    | @@ -0,0 +1,76 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            # This program converts DocBook documents into .epub files.
         | 
| 3 | 
            +
            # 
         | 
| 4 | 
            +
            # Usage: dbtoepub [OPTIONS] [DocBook Files]
         | 
| 5 | 
            +
            #
         | 
| 6 | 
            +
            # .epub is defined by the IDPF at www.idpf.org and is made up of 3 standards:
         | 
| 7 | 
            +
            # - Open Publication Structure (OPS)
         | 
| 8 | 
            +
            # - Open Packaging Format (OPF) 
         | 
| 9 | 
            +
            # - Open Container Format (OCF)
         | 
| 10 | 
            +
            #
         | 
| 11 | 
            +
            # Specific options:
         | 
| 12 | 
            +
            #     -c, --css [FILE]                 Use FILE for CSS on generated XHTML.
         | 
| 13 | 
            +
            #     -d, --debug                      Show debugging output.
         | 
| 14 | 
            +
            #     -f, --font [OTF FILE]            Embed OTF FILE in .epub.
         | 
| 15 | 
            +
            #     -h, --help                       Display usage info.
         | 
| 16 | 
            +
            #     -s, --stylesheet [XSL FILE]      Use XSL FILE as a customization
         | 
| 17 | 
            +
            #                                        layer (imports epub/docbook.xsl).
         | 
| 18 | 
            +
            #     -v, --verbose                    Make output verbose.
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            lib = File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
         | 
| 21 | 
            +
            $LOAD_PATH.unshift(lib) if File.exist?(lib)
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            require 'fileutils'
         | 
| 24 | 
            +
            require 'optparse'
         | 
| 25 | 
            +
            require 'tmpdir'
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            require 'docbook'
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            verbose = false
         | 
| 30 | 
            +
            debug = false
         | 
| 31 | 
            +
            css_file = nil
         | 
| 32 | 
            +
            otf_files = []
         | 
| 33 | 
            +
            customization_layer = nil
         | 
| 34 | 
            +
            output_file = nil
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            #$DEBUG=true
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            # Set up the OptionParser
         | 
| 39 | 
            +
            opts = OptionParser.new
         | 
| 40 | 
            +
            opts.banner = "Usage: #{File.basename($0)} [OPTIONS] [DocBook Files]
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            #{File.basename($0)} converts DocBook <book> and <article>s into to .epub files.
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            .epub is defined by the IDPF at www.idpf.org and is made up of 3 standards:
         | 
| 45 | 
            +
            - Open Publication Structure (OPS)
         | 
| 46 | 
            +
            - Open Packaging Format (OPF) 
         | 
| 47 | 
            +
            - Open Container Format (OCF)
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            Specific options:"
         | 
| 50 | 
            +
            opts.on("-c", "--css [FILE]", "Use FILE for CSS on generated XHTML.") {|f| css_file = f}
         | 
| 51 | 
            +
            opts.on("-d", "--debug", "Show debugging output.") {debug = true; verbose = true}
         | 
| 52 | 
            +
            opts.on("-f", "--font [OTF FILE]", "Embed OTF FILE in .epub.") {|f| otf_files << f}
         | 
| 53 | 
            +
            opts.on("-h", "--help", "Display usage info.") {puts opts.to_s; exit 0}
         | 
| 54 | 
            +
            opts.on("-o", "--output [OUTPUT FILE]", "Output ePub file as OUTPUT FILE.") {|f| output_file = f}
         | 
| 55 | 
            +
            opts.on("-s", "--stylesheet [XSL FILE]", "Use XSL FILE as a customization layer (imports epub/docbook.xsl).") {|f| customization_layer = f}
         | 
| 56 | 
            +
            opts.on("-v", "--verbose", "Make output verbose.") {verbose = true}
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            db_files = opts.parse(ARGV)
         | 
| 59 | 
            +
            if db_files.size == 0
         | 
| 60 | 
            +
              puts opts.to_s
         | 
| 61 | 
            +
              exit 0
         | 
| 62 | 
            +
            end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            db_files.each {|docbook_file|
         | 
| 65 | 
            +
              dir = File.expand_path(File.join(Dir.tmpdir, ".epubtmp#{Time.now.to_f.to_s}"))
         | 
| 66 | 
            +
              FileUtils.mkdir_p(dir)
         | 
| 67 | 
            +
              e = DocBook::Epub.new(docbook_file, dir, css_file, customization_layer, otf_files)
         | 
| 68 | 
            +
             | 
| 69 | 
            +
              if output_file
         | 
| 70 | 
            +
                epub_file = output_file
         | 
| 71 | 
            +
              else  
         | 
| 72 | 
            +
                epub_file = File.basename(docbook_file, ".xml") + ".epub"
         | 
| 73 | 
            +
              end  
         | 
| 74 | 
            +
              puts "Rendering DocBook file #{docbook_file} to #{epub_file}" if verbose
         | 
| 75 | 
            +
              e.render_to_file(epub_file)
         | 
| 76 | 
            +
            }  
         | 
    
        data/lib/docbook.rb
    ADDED
    
    | @@ -0,0 +1,227 @@ | |
| 1 | 
            +
            require 'fileutils'
         | 
| 2 | 
            +
            require 'rexml/parsers/pullparser'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module DocBook
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              class Epub
         | 
| 7 | 
            +
                CHECKER = "epubcheck"
         | 
| 8 | 
            +
                STYLESHEET = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', "docbook.xsl"))
         | 
| 9 | 
            +
                CALLOUT_PATH = File.join('images', 'callouts')
         | 
| 10 | 
            +
                CALLOUT_FULL_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', CALLOUT_PATH))
         | 
| 11 | 
            +
                CALLOUT_LIMIT = 15
         | 
| 12 | 
            +
                CALLOUT_EXT = ".png"
         | 
| 13 | 
            +
                XSLT_PROCESSOR = "xsltproc"
         | 
| 14 | 
            +
                OUTPUT_DIR = ".epubtmp#{Time.now.to_f.to_s}"
         | 
| 15 | 
            +
                MIMETYPE = "application/epub+zip"
         | 
| 16 | 
            +
                META_DIR = "META-INF"
         | 
| 17 | 
            +
                OEBPS_DIR = "OEBPS"
         | 
| 18 | 
            +
                ZIPPER = "zip"
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                attr_reader :output_dir
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def initialize(docbook_file, output_dir=OUTPUT_DIR, css_file=nil, customization_layer=nil, embedded_fonts=[])
         | 
| 23 | 
            +
                  @docbook_file = docbook_file
         | 
| 24 | 
            +
                  @output_dir = output_dir
         | 
| 25 | 
            +
                  @meta_dir  = File.join(@output_dir, META_DIR)
         | 
| 26 | 
            +
                  @oebps_dir = File.join(@output_dir, OEBPS_DIR)
         | 
| 27 | 
            +
                  @css_file = css_file ? File.expand_path(css_file) : css_file
         | 
| 28 | 
            +
                  @embedded_fonts = embedded_fonts
         | 
| 29 | 
            +
                  @to_delete = []
         | 
| 30 | 
            +
                  
         | 
| 31 | 
            +
                  if customization_layer
         | 
| 32 | 
            +
                    @stylesheet = File.expand_path(customization_layer)
         | 
| 33 | 
            +
                  else
         | 
| 34 | 
            +
                    @stylesheet = STYLESHEET
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  unless File.exist?(@docbook_file)
         | 
| 38 | 
            +
                    raise ArgumentError.new("File #{@docbook_file} does not exist")
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def render_to_file(output_file, verbose=false)
         | 
| 43 | 
            +
                  render_to_epub(output_file, verbose)
         | 
| 44 | 
            +
                  bundle_epub(output_file, verbose)
         | 
| 45 | 
            +
                  cleanup_files(@to_delete)
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def self.invalid?(file)
         | 
| 49 | 
            +
                  # Obnoxiously, we can't just check for a non-zero output...
         | 
| 50 | 
            +
                  cmd = %Q(#{CHECKER} "#{file}")
         | 
| 51 | 
            +
                  output = `#{cmd} 2>&1`
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  if $?.to_i == 0
         | 
| 54 | 
            +
                    return false
         | 
| 55 | 
            +
                  else  
         | 
| 56 | 
            +
                    STDERR.puts output if $DEBUG
         | 
| 57 | 
            +
                    return output
         | 
| 58 | 
            +
                  end  
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                private
         | 
| 62 | 
            +
                def render_to_epub(output_file, verbose)  
         | 
| 63 | 
            +
                  @collapsed_docbook_file = collapse_docbook()
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  chunk_quietly =   "--stringparam chunk.quietly " + (verbose ? '0' : '1')
         | 
| 66 | 
            +
                  callout_path =    "--stringparam callout.graphics.path #{CALLOUT_PATH}/"
         | 
| 67 | 
            +
                  callout_limit =   "--stringparam callout.graphics.number.limit #{CALLOUT_LIMIT}"
         | 
| 68 | 
            +
                  callout_ext =     "--stringparam callout.graphics.extension #{CALLOUT_EXT}" 
         | 
| 69 | 
            +
                  html_stylesheet = "--stringparam html.stylesheet #{File.basename(@css_file)}" if @css_file
         | 
| 70 | 
            +
                  base =            "--stringparam base.dir #{OEBPS_DIR}/" 
         | 
| 71 | 
            +
                  unless @embedded_fonts.empty? 
         | 
| 72 | 
            +
                    embedded_fonts = @embedded_fonts.map {|f| File.basename(f)}.join(',')
         | 
| 73 | 
            +
                    font =            "--stringparam epub.embedded.fonts \"#{embedded_fonts}\"" 
         | 
| 74 | 
            +
                  end  
         | 
| 75 | 
            +
                  meta =            "--stringparam epub.metainf.dir #{META_DIR}/" 
         | 
| 76 | 
            +
                  oebps =           "--stringparam epub.oebps.dir #{OEBPS_DIR}/" 
         | 
| 77 | 
            +
                  options = [chunk_quietly, 
         | 
| 78 | 
            +
                             callout_path, 
         | 
| 79 | 
            +
                             callout_limit, 
         | 
| 80 | 
            +
                             callout_ext, 
         | 
| 81 | 
            +
                             base, 
         | 
| 82 | 
            +
                             font, 
         | 
| 83 | 
            +
                             meta, 
         | 
| 84 | 
            +
                             oebps, 
         | 
| 85 | 
            +
                             html_stylesheet,
         | 
| 86 | 
            +
                            ].join(" ")
         | 
| 87 | 
            +
                  # Double-quote stylesheet & file to help Windows cmd.exe
         | 
| 88 | 
            +
                  db2epub_cmd = %Q(cd "#{@output_dir}" && #{XSLT_PROCESSOR} #{options} "#{@stylesheet}" "#{@collapsed_docbook_file}")
         | 
| 89 | 
            +
                  STDERR.puts db2epub_cmd if $DEBUG
         | 
| 90 | 
            +
                  success = system(db2epub_cmd)
         | 
| 91 | 
            +
                  raise "Could not render as .epub to #{output_file} (#{db2epub_cmd})" unless success
         | 
| 92 | 
            +
                  @to_delete << Dir["#{@meta_dir}/*"]
         | 
| 93 | 
            +
                  @to_delete << Dir["#{@oebps_dir}/*"]
         | 
| 94 | 
            +
                end  
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                def bundle_epub(output_file, verbose)  
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  quiet = verbose ? "" : "-q"
         | 
| 99 | 
            +
                  mimetype_filename = write_mimetype()
         | 
| 100 | 
            +
                  meta   = File.basename(@meta_dir)
         | 
| 101 | 
            +
                  oebps  = File.basename(@oebps_dir)
         | 
| 102 | 
            +
                  images = copy_images()
         | 
| 103 | 
            +
                  csses  = copy_csses()
         | 
| 104 | 
            +
                  fonts  = copy_fonts()
         | 
| 105 | 
            +
                  callouts = copy_callouts()
         | 
| 106 | 
            +
                  # zip -X -r ../book.epub mimetype META-INF OEBPS
         | 
| 107 | 
            +
                  # Double-quote stylesheet & file to help Windows cmd.exe
         | 
| 108 | 
            +
                  zip_cmd = %Q(cd "#{@output_dir}" &&  #{ZIPPER} #{quiet} -X -r  "#{File.expand_path(output_file)}" "#{mimetype_filename}" "#{meta}" "#{oebps}")
         | 
| 109 | 
            +
                  puts zip_cmd if $DEBUG
         | 
| 110 | 
            +
                  success = system(zip_cmd)
         | 
| 111 | 
            +
                  raise "Could not bundle into .epub file to #{output_file}" unless success
         | 
| 112 | 
            +
                end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                # Input must be collapsed because REXML couldn't find figures in files that
         | 
| 115 | 
            +
                # were XIncluded or added by ENTITY
         | 
| 116 | 
            +
                #   http://sourceforge.net/tracker/?func=detail&aid=2750442&group_id=21935&atid=373747
         | 
| 117 | 
            +
                def collapse_docbook
         | 
| 118 | 
            +
                  # Double-quote stylesheet & file to help Windows cmd.exe
         | 
| 119 | 
            +
                  collapsed_file = File.join(File.expand_path(File.dirname(@docbook_file)), 
         | 
| 120 | 
            +
                                             '.collapsed.' + File.basename(@docbook_file))
         | 
| 121 | 
            +
                  entity_collapse_command = %Q(xmllint --loaddtd --noent -o "#{collapsed_file}" "#{@docbook_file}")
         | 
| 122 | 
            +
                  entity_success = system(entity_collapse_command)
         | 
| 123 | 
            +
                  raise "Could not collapse named entites in #{@docbook_file}" unless entity_success
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                  xinclude_collapse_command = %Q(xmllint --xinclude -o "#{collapsed_file}" "#{collapsed_file}")
         | 
| 126 | 
            +
                  xinclude_success = system(xinclude_collapse_command)
         | 
| 127 | 
            +
                  raise "Could not collapse XIncludes in #{@docbook_file}" unless xinclude_success
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                  @to_delete << collapsed_file
         | 
| 130 | 
            +
                  return collapsed_file
         | 
| 131 | 
            +
                end  
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                def copy_callouts
         | 
| 134 | 
            +
                  new_callout_images = []
         | 
| 135 | 
            +
                  if has_callouts?
         | 
| 136 | 
            +
                    calloutglob = "#{CALLOUT_FULL_PATH}/*#{CALLOUT_EXT}"
         | 
| 137 | 
            +
                    Dir.glob(calloutglob).each {|img|
         | 
| 138 | 
            +
                      img_new_filename = File.join(@oebps_dir, CALLOUT_PATH, File.basename(img))
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                      # TODO: What to rescue for these two?
         | 
| 141 | 
            +
                      FileUtils.mkdir_p(File.dirname(img_new_filename)) 
         | 
| 142 | 
            +
                      FileUtils.cp(img, img_new_filename)
         | 
| 143 | 
            +
                      @to_delete << img_new_filename
         | 
| 144 | 
            +
                      new_callout_images << img
         | 
| 145 | 
            +
                    }  
         | 
| 146 | 
            +
                  end  
         | 
| 147 | 
            +
                  return new_callout_images
         | 
| 148 | 
            +
                end
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                def copy_fonts
         | 
| 151 | 
            +
                  new_fonts = []
         | 
| 152 | 
            +
                  @embedded_fonts.each {|font_file|
         | 
| 153 | 
            +
                    font_new_filename = File.join(@oebps_dir, File.basename(font_file))
         | 
| 154 | 
            +
                    FileUtils.cp(font_file, font_new_filename)
         | 
| 155 | 
            +
                    new_fonts << font_file
         | 
| 156 | 
            +
                  }
         | 
| 157 | 
            +
                  return new_fonts
         | 
| 158 | 
            +
                end
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                def copy_csses
         | 
| 161 | 
            +
                  if @css_file 
         | 
| 162 | 
            +
                    css_new_filename = File.join(@oebps_dir, File.basename(@css_file))
         | 
| 163 | 
            +
                    FileUtils.cp(@css_file, css_new_filename)
         | 
| 164 | 
            +
                  end
         | 
| 165 | 
            +
                end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                def copy_images
         | 
| 168 | 
            +
                  image_references = get_image_refs()
         | 
| 169 | 
            +
                  new_images = []
         | 
| 170 | 
            +
                  image_references.each {|img|
         | 
| 171 | 
            +
                    # TODO: It'd be cooler if we had a filetype lookup rather than just
         | 
| 172 | 
            +
                    # extension
         | 
| 173 | 
            +
                    if img =~ /\.(svg|png|gif|jpe?g|xml)/i
         | 
| 174 | 
            +
                      img_new_filename = File.join(@oebps_dir, img)
         | 
| 175 | 
            +
                      img_full = File.join(File.expand_path(File.dirname(@docbook_file)), img)
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                      # TODO: What to rescue for these two?
         | 
| 178 | 
            +
                      FileUtils.mkdir_p(File.dirname(img_new_filename)) 
         | 
| 179 | 
            +
                      puts(img_full + ": " + img_new_filename) if $DEBUG
         | 
| 180 | 
            +
                      FileUtils.cp(img_full, img_new_filename)
         | 
| 181 | 
            +
                      @to_delete << img_new_filename
         | 
| 182 | 
            +
                      new_images << img_full
         | 
| 183 | 
            +
                    end
         | 
| 184 | 
            +
                  }  
         | 
| 185 | 
            +
                  return new_images
         | 
| 186 | 
            +
                end
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                def write_mimetype
         | 
| 189 | 
            +
                  mimetype_filename = File.join(@output_dir, "mimetype")
         | 
| 190 | 
            +
                  File.open(mimetype_filename, "w") {|f| f.print MIMETYPE}
         | 
| 191 | 
            +
                  @to_delete << mimetype_filename
         | 
| 192 | 
            +
                  return File.basename(mimetype_filename)
         | 
| 193 | 
            +
                end  
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                def cleanup_files(file_list)
         | 
| 196 | 
            +
                  file_list.flatten.each {|f|
         | 
| 197 | 
            +
                    # Yikes
         | 
| 198 | 
            +
                    FileUtils.rm_r(f, :force => true )
         | 
| 199 | 
            +
                  }  
         | 
| 200 | 
            +
                end  
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                # Returns an Array of all of the (image) @filerefs in a document
         | 
| 203 | 
            +
                def get_image_refs
         | 
| 204 | 
            +
                  parser = REXML::Parsers::PullParser.new(File.new(@collapsed_docbook_file))
         | 
| 205 | 
            +
                  image_refs = []
         | 
| 206 | 
            +
                  while parser.has_next?
         | 
| 207 | 
            +
                    el = parser.pull
         | 
| 208 | 
            +
                    if el.start_element? and (el[0] == "imagedata" or el[0] == "graphic")
         | 
| 209 | 
            +
                      image_refs << el[1]['fileref'] 
         | 
| 210 | 
            +
                    end  
         | 
| 211 | 
            +
                  end
         | 
| 212 | 
            +
                  return image_refs.uniq
         | 
| 213 | 
            +
                end  
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                # Returns true if the document has code callouts
         | 
| 216 | 
            +
                def has_callouts?
         | 
| 217 | 
            +
                  parser = REXML::Parsers::PullParser.new(File.new(@collapsed_docbook_file))
         | 
| 218 | 
            +
                  while parser.has_next?
         | 
| 219 | 
            +
                    el = parser.pull
         | 
| 220 | 
            +
                    if el.start_element? and (el[0] == "calloutlist" or el[0] == "co")
         | 
| 221 | 
            +
                      return true
         | 
| 222 | 
            +
                    end  
         | 
| 223 | 
            +
                  end
         | 
| 224 | 
            +
                  return false
         | 
| 225 | 
            +
                end  
         | 
| 226 | 
            +
              end
         | 
| 227 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,46 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: dbtoepub
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 1.0.0
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Manuel Schnitzer
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2017-11-22 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies: []
         | 
| 13 | 
            +
            description: This program converts DocBook documents into .epub files.
         | 
| 14 | 
            +
            email: webmaster@mschnitzer.de
         | 
| 15 | 
            +
            executables:
         | 
| 16 | 
            +
            - dbtoepub
         | 
| 17 | 
            +
            extensions: []
         | 
| 18 | 
            +
            extra_rdoc_files: []
         | 
| 19 | 
            +
            files:
         | 
| 20 | 
            +
            - bin/dbtoepub
         | 
| 21 | 
            +
            - lib/docbook.rb
         | 
| 22 | 
            +
            homepage: https://github.com/docbook/xslt10-stylesheets
         | 
| 23 | 
            +
            licenses:
         | 
| 24 | 
            +
            - MIT
         | 
| 25 | 
            +
            metadata: {}
         | 
| 26 | 
            +
            post_install_message: 
         | 
| 27 | 
            +
            rdoc_options: []
         | 
| 28 | 
            +
            require_paths:
         | 
| 29 | 
            +
            - lib
         | 
| 30 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 31 | 
            +
              requirements:
         | 
| 32 | 
            +
              - - ">="
         | 
| 33 | 
            +
                - !ruby/object:Gem::Version
         | 
| 34 | 
            +
                  version: '0'
         | 
| 35 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 36 | 
            +
              requirements:
         | 
| 37 | 
            +
              - - ">="
         | 
| 38 | 
            +
                - !ruby/object:Gem::Version
         | 
| 39 | 
            +
                  version: '0'
         | 
| 40 | 
            +
            requirements: []
         | 
| 41 | 
            +
            rubyforge_project: 
         | 
| 42 | 
            +
            rubygems_version: 2.2.5
         | 
| 43 | 
            +
            signing_key: 
         | 
| 44 | 
            +
            specification_version: 4
         | 
| 45 | 
            +
            summary: Hola!
         | 
| 46 | 
            +
            test_files: []
         |