##+########################################################################## # # epubCreator.tsh -- command line tool to create an epub version 3.0 # file from a single text or xhmtml file, an optional cover image and # a list of 0 or more images # by Keith Vetter 2014-03-14 # Mods Clif Flynt, 2014-04-01 # Support for multiple text/html files (multiple chapters) # Support for additional .css file # Support for filename.epub different from "book title.epub" # Support for toc.ncx as well as nav.html # Expanded command line processing # # usage: epubCreator \ # -title Title -author 'last, first' -data file1.txt file2.html file3.xhtml...\ # -cover Cover.jpg -images additional Images -css Style.css -html (1/0) \ # -name BookName # # -title Title for book # -author Name of author as last, first # -data List of data files to include in the text # -cover An image file for the cover # -images Additional images that might be reference by text # -css An optional css file if you want special formatting # -html 1 or 0 to define whether the input data is already in html format # -name The name for the .epub file, if different from title # package require uuid package require fileutil array set EXT {"" "" .png image/png .gif image/gif .jpg image/jpeg .jpeg image/jpeg .svg image/svg+xml} proc parseList {list stateVarName {throwError 1}} { upvar $stateVarName stateArray set errs "" foreach arg $list { if {([string first "-" $arg] == 0) && ([llength $arg] == 1)} { set index [string range $arg 1 end] if {![info exists stateArray($index)]} { if {$throwError} { error "No default for ${stateVarName}($index).\nValid fields are [lsort [array names stateArray]]" } else { lappend errs "No default for ${stateVarName}($index)" } } set cmd set } else { if {[info exists cmd]} { $cmd stateArray($index) $arg set cmd lappend } } } return $errs } proc Init {rawname title author cover images} { global E set guid [::uuid::uuid generate] set E(rawname) $rawname set E(basename) [file tail [file rootname $E(rawname)]] set E(dirname) [file normalize [file dirname $E(rawname)]] set E(output,tempdir) [file join [::fileutil::tempdir] "epub_$guid"] if {$E(name) eq ""} { set E(output,final) [file join $E(dirname) "$E(basename).epub"] } else { set E(output,final) [file join $E(dirname) "$E(name).epub"] } set E(epub) EPUB set E(epub,tempdir) [file join $E(output,tempdir) $E(epub)] set E(html,name) "$E(basename).xhtml" set E(html,tempname) [file join $E(epub,tempdir) "$E(html,name)"] set E(ncx,tempname) [file join $E(epub,tempdir) "toc.ncx"] set E(opf,name) [file join $E(epub) package.opf] set E(opf,tempname) [file join $E(output,tempdir) $E(opf,name)] set E(mimetype) mimetype set E(mimetype,tempname) [file join $E(output,tempdir) $E(mimetype)] set E(meta-inf) META-INF set E(meta-inf,tempdir) [file join $E(output,tempdir) $E(meta-inf)] set E(meta-inf,tempname) [file join $E(meta-inf,tempdir) container.xml] set E(nav,name) nav.xhtml set E(nav,tempname) [file join $E(epub,tempdir) $E(nav,name)] set E(cover,source) $cover set E(cover,name) [file tail $cover] set E(cover,format) $::EXT([file extension $cover]) set E(images) $images set E(date) [clock format [clock seconds] -gmt 1 -format "%Y-%m-%dT%TZ"] file mkdir $E(output,tempdir) file mkdir $E(meta-inf,tempdir) file mkdir $E(epub,tempdir) set E(guid) "ebook:$guid" set E(title) $title set E(author) $author if {$E(css) ne ""} { set E(css,name) $E(css) set E(css,tempname) [file join $E(epub,tempdir) stylesheet.css] set E(opf,stylesheet) {} } } proc MakeEpubFiles {} { global E set i 0 if {$E(cover,source) ne ""} { set E(rawname) [list cover.xhtml {*}$E(rawname)] set of [open cover.xhtml w] puts $of [BodyToHtml ""] close $of } foreach rawname $E(rawname) { incr i set basename [file tail [file rootname $rawname]] set E(html,name) "$basename.xhtml" set E(html,tempname) [file join $E(epub,tempdir) "$E(html,name)"] append E(opf,html_items) [subst { }] append E(opf,ref_items) [subst { }] append navs [subst $::NAV_XHTML1] append ncxs [subst $::CONTENT_NCX1] if {$E(html)} { WriteAllData $E(html,tempname) $rawname } else { WriteAllData $E(html,tempname) [TextToHtml $rawname] } } WriteAllData $E(mimetype,tempname) "application/epub+zip" WriteAllData $E(meta-inf,tempname) [subst $::CONTAINER_XML] WriteAllData $E(opf,tempname) [MakeOPF] WriteAllData $E(nav,tempname) "[subst $::NAV_XHTML0]\n$navs\n$::NAV_XHTML2" WriteAllData $E(ncx,tempname) "[subst $::CONTENT_NCX0]\n$ncxs\n$::CONTENT_NCX2" if {[info exists E(css,tempname)]} { WriteAllData $E(css,tempname) $E(css) } } proc TextToHtml {rawname} { global E set fin [open $rawname r] set data [read $fin] close $fin if {[string first " > \x22 " ' '} $data] ; list regsub -all -line {^$} $data {

} data set data "[subst $::HTML_TEMPLATE]" ; list } return $data } proc BodyToHtml {data} { global E set data "[subst $::HTML_TEMPLATE]" return $data } proc MakeOPF {} { global E set html_items {} set image_items {} set ref_items {} set opf [subst $::CONTENT_OPF] if {$E(cover,source) eq ""} { regsub -all -line {^.*id_cover.*$} $opf "" opf } else { file copy $E(cover,source) $E(epub,tempdir) } ;# Copy any additional images set image_items "" for {set i 0} {$i < [llength $E(images)]} {incr i} { set iname [lindex $E(images) $i] file copy $iname $E(epub,tempdir) set tailname [file tail $iname] set media $::EXT([file extension $iname]) set id "id_image_$i" set line "\n" append image_items $line } if {$image_items ne ""} { regsub -line {^.*other images go here.*$} $opf $image_items opf } return $opf } proc ZipEpub {} { global E cd $E(output,tempdir) # Get rid of any previous epub else zip will append to the # existing file, leaving behind elements you thought had been removed catch {file delete $E(output,final)} exec zip -rX $E(output,final) $E(mimetype) $E(meta-inf)/ $E(epub)/ } proc WriteAllData {fname data} { if {[file exists $data]} { set if [open $data] set data [read $if] close $if } set fout [open $fname w]; puts -nonewline $fout $data; close $fout; } proc Cleanup {} { global E file delete -force -- $E(output,tempdir) file delete cover.xhtml } set HTML_TEMPLATE { $E(title)

$data

} set CONTAINER_XML { } # NCX format # per: http://www.gbenthien.net/Kindle%20and%20EPUB/ncx.html set CONTENT_NCX0 { $::E(name) $::E(author) } set CONTENT_NCX1 { Chapter $i } set CONTENT_NCX2 { } set CONTENT_OPF { $E(title) $E(author) $E(guid) en $E(date) $::E(opf,stylesheet) $::E(opf,html_items) $image_items $::E(opf,ref_items) } set NAV_XHTML0 { $::E(name) } puts "\nepubCreator v0.3\nby Keith Vetter\n" if {[llength $argv] < 3} { puts stderr "usage: epubCreator -title -author <author> -data <data file1> <data file2> ... -cover ?<cover image>? -images ?<other images> ...?" puts stderr "for example:" puts stderr " epubCreator \"Pride and Prejudice\" \"Austen, Jane\" p_and_p.xhtml cover.jpg chapter1.html" return } # set images [lassign $argv title author fname cover] array set E { title {} author {} data {} cover {} images {} css {} html {0} name {} } parseList $argv E 1 Init $E(data) $E(title) $E(author) $E(cover) $E(images) MakeEpubFiles # parray E # puts "TMP: ..$E(output,tempdir).." ZipEpub Cleanup puts "created $E(output,final)" return