diff --git a/data/style/epubdefault.css b/data/style/epubdefault.css new file mode 100644 index 000000000..178a7edc3 --- /dev/null +++ b/data/style/epubdefault.css @@ -0,0 +1,15 @@ +div.recipe, div.index { font-style: Times, Serif; font-size: 12pt; margin-left: 7%; margin-right: 10%; padding: 1em; margin-top: 1em;} +div.recipe img {float: right; padding: 1em} +body {} +span.label { font-weight: bold;min-width: 10em; display: inline-block;} +p.title { font-size: 120%; text-align: center} +p.title span.label {display: none} +div.header p {margin-top: 0; margin-bottom: 0.2em} +div.ing { padding: 1em; border: solid 1px; background-color: #eef} +ul.ing { padding-left: 0.6em; } +li.ing { list-style: none; border-top: 0.3em } +div.ingamount{ display:inline-block; min-width:3em;} +div.ingunit{ display:inline-block; min-width:3em;} +img{ max-width: 90%; display: block; margin-left: auto; margin-right: auto; } +div.header{margin-top: 1em; margin-bottom: 1em;} +div.ing h2{margin-top: 0} diff --git a/gourmet/plugins/import_export/epub.gourmet-plugin b/gourmet/plugins/import_export/epub.gourmet-plugin new file mode 100644 index 000000000..0976066ce --- /dev/null +++ b/gourmet/plugins/import_export/epub.gourmet-plugin @@ -0,0 +1,8 @@ +[Gourmet Plugin] +Module=epub_plugin +Version=1.0 +API_Version=1.0 +_Name=EPub Export +_Comment=Create an epub book from recipes. +_Category=Importer/Exporter +Authors=Sven Steckmann diff --git a/gourmet/plugins/import_export/epub_plugin/__init__.py b/gourmet/plugins/import_export/epub_plugin/__init__.py new file mode 100644 index 000000000..e8fac24c0 --- /dev/null +++ b/gourmet/plugins/import_export/epub_plugin/__init__.py @@ -0,0 +1,3 @@ +import epub_exporter_plugin + +plugins = [epub_exporter_plugin.EpubExporterPlugin] diff --git a/gourmet/plugins/import_export/epub_plugin/epub_exporter.py b/gourmet/plugins/import_export/epub_plugin/epub_exporter.py new file mode 100644 index 000000000..4888fd3a4 --- /dev/null +++ b/gourmet/plugins/import_export/epub_plugin/epub_exporter.py @@ -0,0 +1,302 @@ +import os, re +import xml.sax.saxutils +from gettext import gettext as _ +from gourmet import convert,gglobals +from gourmet.exporters.exporter import ExporterMultirec, exporter_mult + +from ebooklib import epub +from string import Template + +RECIPE_HEADER = Template(''' + + + + $title + + +''') +RECIPE_FOOT= "" + +EPUB_DEFAULT_CSS="epubdefault.css" + + +class EpubWriter(): + """This class contains all things to write an epub and is a small wrapper + around the EbookLib which is capable of producing epub files and maybe + kindle in the future (it is under heavy development). + """ + def __init__(self, outFileName): + """ + @param outFileName The filename + path the ebook is written to on finish. + """ + self.outFileName = outFileName + self.lang = "en" + self.recipeCss = None + + self.imgCount = 0 + self.recipeCount = 0 + + self.ebook = epub.EpubBook() + self.spine = ['nav'] + self.toc = [] + + # set metadata + self.ebook.set_identifier("Cookme") # TODO: Something meaningful or time? + self.ebook.set_title("My Cookbook") + self.ebook.set_language(self.lang) + + # This is normally the publisher, makes less sense for the moment + # ebook.add_metadata('DC', 'publisher', "Gourmet") + + # TODO: Add real author from somewhere + self.ebook.add_author("Gourmet") + + # This adds the field also known as keywords in some programs. + self.ebook.add_metadata('DC', 'subject', "cooking") + + def addRecipeCssFromFile(self, filename): + """ Adds the CSS file from filename to the book. The style will be added + to the books root. + @param filename The file the css is read from and attached + @return The internal name within the ebook to reference this file. + """ + cssFileName = "Style/recipe.css" + + style = open(filename, 'rb').read() + recipe_css = epub.EpubItem( uid="style" + , file_name=cssFileName + , media_type="text/css" + , content=style) + self.ebook.add_item(recipe_css) + self.recipeCss = recipe_css + return cssFileName; + + def addJpegImage(self, imageData): + """Adds a jpeg image from the imageData array to the book and returns + the reference name for the image to be used in html. + @param imageData Image data in format jpeg + @return The name of the image to be used in html + """ + epimg = epub.EpubImage() + epimg.file_name = "grf/image_%i.jpg" % self.imgCount + self.imgCount += 1 + epimg.media_type = "image/jpeg" + epimg.set_content(imageData) + self.ebook.add_item(epimg) + return epimg.file_name; + + def getFileForRecipeID(self, id, ext=".xhtml"): + """ + Returns a filename to reference a specific recipe + @param id The id which is also passed during addRecipeText + @return A filename for reference + """ + return "recipe_%i%s" % (id,ext) + + def addRecipeText(self, uniqueId, title, text): + """ Adds the recipe text as a chapter. + """ + uniqueName = self.getFileForRecipeID(uniqueId, ext="") + fileName = self.getFileForRecipeID(uniqueId) + self.recipeCount += 1 + + c1 = epub.EpubHtml(title=title, file_name=fileName, lang=self.lang) + c1.content = text.encode('utf-8') + c1.add_item( self.recipeCss ) + + # add chapter + self.ebook.add_item(c1) + self.spine.append(c1) + + # define Table Of Contents + self.toc.append( epub.Link(fileName, title, uniqueName) ) + + def finish(self): + """ Finish the book and writes it to the disk. + """ + self.ebook.toc = self.toc + + # add default NCX and Nav file + self.ebook.add_item(epub.EpubNcx()) + self.ebook.add_item(epub.EpubNav()) + + self.ebook.spine = self.spine + + epub.write_epub(self.outFileName, self.ebook, {}) + +class epub_exporter (exporter_mult): + def __init__ (self, rd, r, out, conv=None, + doc=None, + # exporter_mult args + mult=1, + change_units=True, + ): + self.doc = doc + + # This document will be appended by the strings to join them in the + # last step and pass it to the ebookwriter. + self.preparedDocument = [] + + #self.link_generator=link_generator + exporter_mult.__init__(self, rd, r, out, + conv=conv, + imgcount=1, + mult=mult, + change_units=change_units, + do_markup=True, + use_ml=True) + + def htmlify (self, text): + t=text.strip() + #t=xml.sax.saxutils.escape(t) + t="

%s

"%t + t=re.sub('\n\n+','

',t) + t=re.sub('\n','
',t) + return t + + def get_title(self): + """Returns the title of the book in an unescaped format""" + title = self._grab_attr_(self.r,'title') + if not title: title = _('Recipe') + return unicode(title) + + def write_head (self): + self.preparedDocument.append( + RECIPE_HEADER.substitute(title=self.get_title()) ) + self.preparedDocument.append("

%s

" + % xml.sax.saxutils.escape(self.get_title())) + + def write_image (self, image): + imagePath = self.doc.addJpegImage( image) + self.preparedDocument.append('' % imagePath) + + def write_inghead (self): + self.preparedDocument.append('

%s

\n
\n') + + def write_foot (self): + self.preparedDocument.append(RECIPE_FOOT) + + self._grab_attr_(self.r,'id') + self.doc.addRecipeText(self._grab_attr_(self.r,'id'), self.get_title(), "".join(self.preparedDocument) ) + +class website_exporter (ExporterMultirec): + def __init__ (self, rd, recipe_table, out, conv=None, ext='epub', copy_css=True, + css=os.path.join(gglobals.style_dir,EPUB_DEFAULT_CSS), + index_rows=['title','category','cuisine','rating','yields'], + change_units=False, + mult=1): + + self.doc = EpubWriter(out) + self.doc.addRecipeCssFromFile(css) + + self.ext=ext + + self.index_rows=index_rows + self.exportargs={ 'change_units':change_units, + 'mult':mult, + 'doc':self.doc} + + if conv: + self.exportargs['conv']=conv + ExporterMultirec.__init__(self, rd, recipe_table, out, + one_file=True, + open_files=False, + ext=self.ext, + exporter=epub_exporter, + exporter_kwargs=self.exportargs) + + def recipe_hook (self, rec, filename, exporter): + """Add index entry""" + # TODO: Do some cool things here. + pass + + def write_footer (self): + self.doc.finish() + diff --git a/gourmet/plugins/import_export/epub_plugin/epub_exporter_plugin.py b/gourmet/plugins/import_export/epub_plugin/epub_exporter_plugin.py new file mode 100644 index 000000000..6fbae03f2 --- /dev/null +++ b/gourmet/plugins/import_export/epub_plugin/epub_exporter_plugin.py @@ -0,0 +1,27 @@ +from gourmet.plugin import ExporterPlugin +import epub_exporter +from gettext import gettext as _ + +EPUBFILE = _('Epub File') + +class EpubExporterPlugin (ExporterPlugin): + label = _('Exporting epub') + sublabel = _('Exporting recipes an epub file in directory %(file)s') + single_completed_string = _('Recipe saved as epub file %(file)s') + filetype_desc = EPUBFILE + saveas_filters = [EPUBFILE,['application/epub+zip'],['*.epub']] + saveas_single_filters = [EPUBFILE,['application/epub+zip'],['*.epub']] + + def get_multiple_exporter (self, args): + return epub_exporter.website_exporter( + args['rd'], + args['rv'], + args['file'], + #args['conv'], + ) + + def do_single_export (self, args) : + print "Single export not supported!" + + def run_extra_prefs_dialog (self): + pass