Skip to content

Instantly share code, notes, and snippets.

@voodoojello
Last active April 28, 2021 12:51
Show Gist options
  • Save voodoojello/75bf1862417e869d2108 to your computer and use it in GitHub Desktop.
Save voodoojello/75bf1862417e869d2108 to your computer and use it in GitHub Desktop.
Create ebook (EPUB) from PNG, JPG, or GIF images
#!/usr/bin/perl -w
#
my $app = {
'name' => 'img2epub',
'desc' => 'Create ebook (EPUB) from PNG, JPG, or GIF images',
'modified' => 'Mon Aug 15 06:03:05 CDT 2011',
'author' => 'mark page [[email protected]]',
'version' => '0.0.1.4b',
};
my ($manifest,$spine,$uuid,$firstpage,$coverimg);
print '=' x 80 . "\n";
print ' ' . $app->{'name'} . ' v' . $app->{'version'} . ' - ' . $app->{'desc'} . "\n";
print ' ' . $app->{'modified'} . ' - ' . $app->{'author'} . "\n";
print '=' x 80 . "\n\n";
print ' Path: '; chomp(my $path = <STDIN>);
print ' Title: '; chomp(my $title = <STDIN>);
print ' Author: '; chomp(my $author = <STDIN>);
unless ($title & $path & $author) { die 'Not enough input!'; }
unless (stat("$path")) { die 'Invalid image path!'; }
print "\n";
print ' >> Creating EPUB Directory structure.' . "\n";
`mkdir ./"$title"`;
`mkdir ./"$title"/META-INF`;
`mkdir ./"$title"/OEBPS`;
`mkdir ./"$title"/OEBPS/Text`;
`mkdir ./"$title"/OEBPS/Images`;
print ' >> Generating local (pseudo) UUID.' . "\n";
my $uuid_raw = '00000000-0000-0000-0000-000000000000';
for my $i (0 .. length($uuid_raw)) {
if (substr($uuid_raw,$i,1) ne '-') {
$uuid .= int(rand(9));
}
else {
$uuid .= substr($uuid_raw,$i,1);
}
}
print ' >> Copying Images and Writing OEBPS data...' . "\n";
chomp(my @imgs = `ls "$path"/*.png *.jpg *.gif 2>/dev/null`); #*/
for $i (0 .. (scalar(@imgs) - 1)) {
my $imgname = (split('/',$imgs[$i]))[-1];
my $imgtype = substr($imgname,-3,3);
my $alttype = (split('_',$imgname))[0];
my $number = (split('_',$imgname))[-1];
$number =~ s/\.png//; $number =~ s/\.jpg//; $number =~ s/\.gif//;
if ($number =~ m/0000/i or $number =~ m/cover/i) {
$alttype = 'cover';
$firstpage = 'content' . $number . '.xhtml';
$coverimg = $imgname;
}
else {
$alttype = $alttype . $number;
}
`cp "$imgs[$i]" ./"$title"/OEBPS/Images/$imgname`;
$manifest .= "\t\t" . '<item href="Images/' . $imgname . '" id="' . $imgname . '" media-type="image/' . $imgtype . '" />' . "\n";
$manifest .= "\t\t" . '<item href="Text/content' . $number . '.xhtml" id="content' . $number . '.xhtml" media-type="application/xhtml+xml" />' . "\n";
$spine .= "\t\t" . '<itemref idref="content' . $number . '.xhtml" />' . "\n";
open (XML,'>',"./$title/OEBPS/Text/content$number.xhtml");
print XML '<?xml version="1.0" encoding="utf-8" standalone="no"?>' . "\n";
print XML '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"' . "\n";
print XML ' "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' . "\n\n";
print XML '<html xmlns="http://www.w3.org/1999/xhtml">' . "\n";
print XML '<head>' . "\n";
print XML ' <title></title>' . "\n";
print XML '</head>' . "\n\n";
print XML '<body>' . "\n";
print XML ' <div><img alt="' . $alttype . '" src="../Images/' . $imgname . '" /></div>' . "\n";
print XML '</body>' . "\n";
print XML '</html>' . "\n";
close(XML);
}
# mimetype
print ' >> Writing MIMETYPE data identifier' . "\n";
open(MIME,'>',"./$title/mimetype");
print MIME 'application/epub+zip';
close(MIME);
# container.xml
print ' >> Writing META-INF (container) data.' . "\n";
open(META,'>',"./$title/META-INF/container.xml");
print META '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
print META '<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">' . "\n";
print META ' <rootfiles>' . "\n";
print META ' <rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/>' . "\n";
print META ' </rootfiles>' . "\n";
print META '</container>' . "\n";
close(META);
# toc.ncx
print ' >> Writing TOC/NCX data.' . "\n";
open(TOC,'>',"./$title/OEBPS/toc.ncx");
print TOC '<?xml version="1.0" encoding="utf-8"?>' . "\n";
print TOC '<!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN"' . "\n";
print TOC ' "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">' . "\n\n";
print TOC '<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">' . "\n";
print TOC '<head>' . "\n";
print TOC ' <meta name="dtb:uid" content="urn:uuid:' . $uuid . '" />' . "\n";
print TOC ' <meta name="dtb:depth" content="0" />' . "\n";
print TOC ' <meta name="dtb:totalPageCount" content="0" />' . "\n";
print TOC ' <meta name="dtb:maxPageNumber" content="0" />' . "\n";
print TOC '</head>' . "\n";
print TOC '<docTitle>' . "\n";
print TOC ' <text></text>' . "\n";
print TOC '</docTitle>' . "\n";
print TOC '<navMap>' . "\n";
print TOC ' <navPoint id="navPoint-1" playOrder="1">' . "\n";
print TOC ' <navLabel>' . "\n";
print TOC ' <text>' . $title . '</text>' . "\n";
print TOC ' </navLabel>' . "\n";
print TOC ' <content src="Text/' . $firstpage . '" />' . "\n";
print TOC ' </navPoint>' . "\n";
print TOC '</navMap>' . "\n";
print TOC '</ncx>' . "\n";
close(TOC);
# content.opf
print ' >> Writing OPF content data.' . "\n";
open(OPF,'>',"./$title/OEBPS/content.opf");
print OPF '<?xml version="1.0" encoding="utf-8" standalone="yes"?>' . "\n";
print OPF '<package xmlns="http://www.idpf.org/2007/opf" unique-identifier="BookId" version="2.0">' . "\n";
print OPF ' <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">' . "\n";
print OPF ' <dc:identifier id="BookId" opf:scheme="UUID">urn:uuid:' . $uuid . '</dc:identifier>' . "\n";
print OPF ' <dc:title>' . $title . '</dc:title>' . "\n";
print OPF ' <dc:creator opf:role="aut">' . $author . '</dc:creator>' . "\n";
print OPF ' <dc:language>en</dc:language>' . "\n";
print OPF ' <meta content="' . $coverimg . '" name="cover" />' . "\n";
print OPF ' </metadata>' . "\n";
print OPF ' <manifest>' . "\n";
print OPF ' <item href="toc.ncx" id="ncx" media-type="application/x-dtbncx+xml" />' . "\n";
print OPF $manifest . "\n";
print OPF ' </manifest>' . "\n";
print OPF ' <spine toc="ncx">' . "\n";
print OPF $spine . "\n";
print OPF ' </spine>' . "\n";
print OPF ' <guide>' . "\n";
print OPF ' <reference href="Text/' . $firstpage . '" title="Cover" type="cover" />' . "\n";
print OPF ' </guide>' . "\n";
print OPF '</package>' . "\n";
close(OPF);
print ' >> Compressing EPUB...' . "\n";
`cd ./"$title"/ \&\& zip -r -0 "../$title\.epub" *`;
print ' >> Done.' . "\n\n";
exit;
__END__
@voodoojello
Copy link
Author

This script was designed to be used in conjunction with GIMP and the "Export Layers as PNG" plugin (http://registry.gimp.org/node/18440). Images should be sized for device, non-rotated, portrait recommended. Cover may be designated with a "0000" numerical name context or by simply using the text "cover" anywhere in the file name.

Note: This will run on Linux or MacOS. Requires a base Cygwin installation on Win32/64.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment