diff options
author | Remko Tronçon <git@el-tramo.be> | 2010-03-28 19:43:32 (GMT) |
---|---|---|
committer | Remko Tronçon <git@el-tramo.be> | 2010-03-28 19:44:49 (GMT) |
commit | 3afa4f741c26360245dc313bc368f508b26a6b96 (patch) | |
tree | 4fc9fedac990a099010fed8a4ee09ddc51c21cc5 /3rdParty/DocBook/XSL/epub/bin/lib/docbook.rb | |
parent | 7548dabae7d10e48816142e508be651ada9f7bc3 (diff) | |
download | swift-contrib-3afa4f741c26360245dc313bc368f508b26a6b96.zip swift-contrib-3afa4f741c26360245dc313bc368f508b26a6b96.tar.bz2 |
Added DocBook infrastructure.
Imported most of "DocBook kit".
Added placeholder for Swiften developers guide.
Diffstat (limited to '3rdParty/DocBook/XSL/epub/bin/lib/docbook.rb')
-rwxr-xr-x | 3rdParty/DocBook/XSL/epub/bin/lib/docbook.rb | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/3rdParty/DocBook/XSL/epub/bin/lib/docbook.rb b/3rdParty/DocBook/XSL/epub/bin/lib/docbook.rb new file mode 100755 index 0000000..ef72ef8 --- /dev/null +++ b/3rdParty/DocBook/XSL/epub/bin/lib/docbook.rb @@ -0,0 +1,226 @@ +require 'fileutils' +require 'rexml/parsers/pullparser' + +module DocBook + + class Epub + CHECKER = "epubcheck" + STYLESHEET = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', "docbook.xsl")) + CALLOUT_PATH = File.join('images', 'callouts') + CALLOUT_FULL_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', CALLOUT_PATH)) + CALLOUT_LIMIT = 15 + CALLOUT_EXT = ".png" + XSLT_PROCESSOR = "xsltproc" + OUTPUT_DIR = ".epubtmp#{Time.now.to_f.to_s}" + MIMETYPE = "application/epub+zip" + META_DIR = "META-INF" + OEBPS_DIR = "OEBPS" + ZIPPER = "zip" + + attr_reader :output_dir + + def initialize(docbook_file, output_dir=OUTPUT_DIR, css_file=nil, customization_layer=nil, embedded_fonts=[]) + @docbook_file = docbook_file + @output_dir = output_dir + @meta_dir = File.join(@output_dir, META_DIR) + @oebps_dir = File.join(@output_dir, OEBPS_DIR) + @css_file = css_file ? File.expand_path(css_file) : css_file + @embedded_fonts = embedded_fonts + raise NotImplementedError if @embedded_fonts.length > 1 + @to_delete = [] + + if customization_layer + @stylesheet = File.expand_path(customization_layer) + else + @stylesheet = STYLESHEET + end + + unless File.exist?(@docbook_file) + raise ArgumentError.new("File #{@docbook_file} does not exist") + end + end + + def render_to_file(output_file, verbose=false) + render_to_epub(output_file, verbose) + bundle_epub(output_file, verbose) + cleanup_files(@to_delete) + end + + def self.invalid?(file) + # Obnoxiously, we can't just check for a non-zero output... + cmd = "#{CHECKER} #{file}" + output = `#{cmd} 2>&1` + + if $?.to_i == 0 + return false + else + STDERR.puts output if $DEBUG + return output + end + end + + private + def render_to_epub(output_file, verbose) + @collapsed_docbook_file = collapse_docbook() + + chunk_quietly = "--stringparam chunk.quietly " + (verbose ? '0' : '1') + callout_path = "--stringparam callout.graphics.path #{CALLOUT_PATH}/" + callout_limit = "--stringparam callout.graphics.number.limit #{CALLOUT_LIMIT}" + callout_ext = "--stringparam callout.graphics.extension #{CALLOUT_EXT}" + html_stylesheet = "--stringparam html.stylesheet #{File.basename(@css_file)}" if @css_file + base = "--stringparam base.dir #{@oebps_dir}/" + unless @embedded_fonts.empty? + font = "--stringparam epub.embedded.font \"#{File.basename(@embedded_fonts.first)}\"" + end + meta = "--stringparam epub.metainf.dir #{@meta_dir}/" + oebps = "--stringparam epub.oebps.dir #{@oebps_dir}/" + options = [chunk_quietly, + callout_path, + callout_limit, + callout_ext, + base, + font, + meta, + oebps, + html_stylesheet, + ].join(" ") + # Double-quote stylesheet & file to help Windows cmd.exe + db2epub_cmd = "#{XSLT_PROCESSOR} #{options} \"#{@stylesheet}\" \"#{@collapsed_docbook_file}\"" + STDERR.puts db2epub_cmd if $DEBUG + success = system(db2epub_cmd) + raise "Could not render as .epub to #{output_file} (#{db2epub_cmd})" unless success + @to_delete << Dir["#{@meta_dir}/*"] + @to_delete << Dir["#{@oebps_dir}/*"] + end + + def bundle_epub(output_file, verbose) + + quiet = verbose ? "" : "-q" + mimetype_filename = write_mimetype() + meta = File.basename(@meta_dir) + oebps = File.basename(@oebps_dir) + images = copy_images() + csses = copy_csses() + fonts = copy_fonts() + callouts = copy_callouts() + # zip -X -r ../book.epub mimetype META-INF OEBPS + # Double-quote stylesheet & file to help Windows cmd.exe + zip_cmd = "cd \"#{@output_dir}\" && #{ZIPPER} #{quiet} -X -r \"#{File.expand_path(output_file)}\" \"#{mimetype_filename}\" \"#{meta}\" \"#{oebps}\"" + puts zip_cmd if $DEBUG + success = system(zip_cmd) + raise "Could not bundle into .epub file to #{output_file}" unless success + end + + # Input must be collapsed because REXML couldn't find figures in files that + # were XIncluded or added by ENTITY + # http://sourceforge.net/tracker/?func=detail&aid=2750442&group_id=21935&atid=373747 + def collapse_docbook + collapsed_file = File.join(File.dirname(@docbook_file), + '.collapsed.' + File.basename(@docbook_file)) + entity_collapse_command = "xmllint --loaddtd --noent -o '#{collapsed_file}' '#{@docbook_file}'" + entity_success = system(entity_collapse_command) + raise "Could not collapse named entites in #{@docbook_file}" unless entity_success + + xinclude_collapse_command = "xmllint --xinclude -o '#{collapsed_file}' '#{collapsed_file}'" + xinclude_success = system(xinclude_collapse_command) + raise "Could not collapse XIncludes in #{@docbook_file}" unless xinclude_success + + @to_delete << collapsed_file + return collapsed_file + end + + def copy_callouts + new_callout_images = [] + if has_callouts? + calloutglob = "#{CALLOUT_FULL_PATH}/*#{CALLOUT_EXT}" + Dir.glob(calloutglob).each {|img| + img_new_filename = File.join(@oebps_dir, CALLOUT_PATH, File.basename(img)) + + # TODO: What to rescue for these two? + FileUtils.mkdir_p(File.dirname(img_new_filename)) + FileUtils.cp(img, img_new_filename) + @to_delete << img_new_filename + new_callout_images << img + } + end + return new_callout_images + end + + def copy_fonts + new_fonts = [] + @embedded_fonts.each {|font_file| + font_new_filename = File.join(@oebps_dir, File.basename(font_file)) + FileUtils.cp(font_file, font_new_filename) + new_fonts << font_file + } + return new_fonts + end + + def copy_csses + if @css_file + css_new_filename = File.join(@oebps_dir, File.basename(@css_file)) + FileUtils.cp(@css_file, css_new_filename) + end + end + + def copy_images + image_references = get_image_refs() + new_images = [] + image_references.each {|img| + # TODO: It'd be cooler if we had a filetype lookup rather than just + # extension + if img =~ /\.(svg|png|gif|jpe?g|xml)/i + img_new_filename = File.join(@oebps_dir, img) + img_full = File.join(File.expand_path(File.dirname(@docbook_file)), img) + + # TODO: What to rescue for these two? + FileUtils.mkdir_p(File.dirname(img_new_filename)) + puts(img_full + ": " + img_new_filename) if $DEBUG + FileUtils.cp(img_full, img_new_filename) + @to_delete << img_new_filename + new_images << img_full + end + } + return new_images + end + + def write_mimetype + mimetype_filename = File.join(@output_dir, "mimetype") + File.open(mimetype_filename, "w") {|f| f.print MIMETYPE} + @to_delete << mimetype_filename + return File.basename(mimetype_filename) + end + + def cleanup_files(file_list) + file_list.flatten.each {|f| + # Yikes + FileUtils.rm_r(f, :force => true ) + } + end + + # Returns an Array of all of the (image) @filerefs in a document + def get_image_refs + parser = REXML::Parsers::PullParser.new(File.new(@collapsed_docbook_file)) + image_refs = [] + while parser.has_next? + el = parser.pull + if el.start_element? and (el[0] == "imagedata" or el[0] == "graphic") + image_refs << el[1]['fileref'] + end + end + return image_refs + end + + # Returns true if the document has code callouts + def has_callouts? + parser = REXML::Parsers::PullParser.new(File.new(@collapsed_docbook_file)) + while parser.has_next? + el = parser.pull + if el.start_element? and (el[0] == "calloutlist" or el[0] == "co") + return true + end + end + return false + end + end +end |