diff --git a/src/calibre/ebooks/oeb/polish/import_book.py b/src/calibre/ebooks/oeb/polish/import_book.py new file mode 100644 index 0000000000..7fa4fbf80c --- /dev/null +++ b/src/calibre/ebooks/oeb/polish/import_book.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2014, Kovid Goyal ' + +import os, sys + +from calibre.ptempfile import TemporaryDirectory +from calibre.ebooks.conversion.plumber import Plumber +from calibre.ebooks.oeb.polish.container import Container, OEB_DOCS, OEB_STYLES +from calibre.ebooks.epub import initialize_container + +from calibre.utils.logging import default_log + +def auto_fill_manifest(container): + manifest_id_map = container.manifest_id_map + manifest_name_map = {v:k for k, v in manifest_id_map.iteritems()} + + for name, mt in container.mime_map.iteritems(): + if name not in manifest_name_map and not container.ok_to_be_unmanifested(name): + mitem = container.generate_item(name) + gname = container.href_to_name(mitem.get('href'), container.opf_name) + if gname != name: + raise ValueError('This should never happen (gname=%r, name=%r)' % (gname, name)) + manifest_name_map[name] = mitem.get('id') + manifest_id_map[mitem.get('id')] = name + +def import_book_as_epub(srcpath, destpath, log=default_log): + if not destpath.lower().endswith('.epub'): + raise ValueError('Can only import books into the EPUB format, not %s' % (os.path.basename(destpath))) + with TemporaryDirectory('eei') as tdir: + plumber = Plumber(srcpath, tdir, log) + plumber.setup_options() + if srcpath.lower().endswith('.opf'): + plumber.opts.dont_package = True + if hasattr(plumber.opts, 'no_process'): + plumber.opts.no_process = True + plumber.input_plugin.for_viewer = True + with plumber.input_plugin, open(plumber.input, 'rb') as inf: + pathtoopf = plumber.input_plugin(inf, plumber.opts, plumber.input_fmt, log, {}, tdir) + if hasattr(pathtoopf, 'manifest'): + from calibre.ebooks.oeb.iterator.book import write_oebbook + pathtoopf = write_oebbook(pathtoopf, tdir) + + c = Container(tdir, pathtoopf, log) + auto_fill_manifest(c) + # Auto fix all HTML/CSS + for name, mt in c.mime_map.iteritems(): + if mt in set(OEB_DOCS) | set(OEB_STYLES): + c.parsed(name) + c.dirty(name) + c.commit() + + zf = initialize_container(destpath, opf_name=c.opf_name) + with zf: + for name in c.name_path_map: + zf.writestr(name, c.raw_data(name, decode=False)) + +if __name__ == '__main__': + import_book_as_epub(sys.argv[-2], sys.argv[-1]) + diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index 652b6617b4..6405d6bf90 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -37,7 +37,7 @@ from calibre.gui2.tweak_book.toc import TOCEditor from calibre.gui2.tweak_book.editor import editor_from_syntax, syntax_from_mime from calibre.gui2.tweak_book.editor.insert_resource import get_resource_data, NewBook from calibre.gui2.tweak_book.preferences import Preferences -from calibre.gui2.tweak_book.widgets import RationalizeFolders, MultiSplit +from calibre.gui2.tweak_book.widgets import RationalizeFolders, MultiSplit, ImportForeign _diff_dialogs = [] @@ -167,6 +167,19 @@ class Boss(QObject): create_book(d.mi, path, fmt=fmt) self.open_book(path=path) + def import_book(self): + if not self._check_before_open(): + return + d = ImportForeign(self.gui) + if d.exec_() == d.Accepted: + from calibre.ebooks.oeb.polish.import_book import import_book_as_epub + src, dest = d.data + self._clear_notify_data = True + def func(src, dest, tdir): + import_book_as_epub(src, dest) + return get_container(dest, tdir=tdir) + self.gui.blocking_job('import_book', _('Importing book, please wait...'), self.book_opened, func, src, dest, tdir=self.mkdtemp()) + def open_book(self, path=None, edit_file=None, clear_notify_data=True): if not self._check_before_open(): return diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index baef400254..872998eef1 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -299,6 +299,8 @@ class Main(MainWindow): self.action_quit = reg('quit.png', _('&Quit'), self.boss.quit, 'quit', 'Ctrl+Q', _('Quit')) self.action_preferences = reg('config.png', _('&Preferences'), self.boss.preferences, 'preferences', 'Ctrl+P', _('Preferences')) self.action_new_book = reg('book.png', _('Create &new, empty book'), self.boss.new_book, 'new-book', (), _('Create a new, empty book')) + self.action_import_book = reg('book.png', _('&Import an HTML or DOCX file as a new book'), + self.boss.import_book, 'import-book', (), _('Import an HTML or DOCX file as a new book')) # Editor actions group = _('Editor actions') @@ -429,6 +431,7 @@ class Main(MainWindow): f.addAction(self.action_import_files) f.addAction(self.action_open_book) f.addAction(self.action_new_book) + f.addAction(self.action_import_book) self.recent_books_menu = f.addMenu(_('&Recently opened books')) self.update_recent_books() f.addSeparator() diff --git a/src/calibre/gui2/tweak_book/widgets.py b/src/calibre/gui2/tweak_book/widgets.py index e2dc4824aa..88ac937d2a 100644 --- a/src/calibre/gui2/tweak_book/widgets.py +++ b/src/calibre/gui2/tweak_book/widgets.py @@ -7,9 +7,10 @@ __license__ = 'GPL v3' __copyright__ = '2014, Kovid Goyal ' from PyQt4.Qt import ( - QDialog, QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QVBoxLayout) + QDialog, QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QVBoxLayout, + QFormLayout, QHBoxLayout, QToolButton, QIcon, QApplication, Qt) -from calibre.gui2 import error_dialog +from calibre.gui2 import error_dialog, choose_files, choose_save_file from calibre.gui2.tweak_book import tprefs class Dialog(QDialog): @@ -140,3 +141,86 @@ class MultiSplit(Dialog): # {{{ return self._xpath.xpath # }}} + +class ImportForeign(Dialog): # {{{ + + def __init__(self, parent=None): + Dialog.__init__(self, _('Choose file to import'), 'import-foreign') + + def sizeHint(self): + ans = Dialog.sizeHint(self) + ans.setWidth(ans.width() + 200) + return ans + + def setup_ui(self): + self.l = l = QFormLayout(self) + self.setLayout(l) + + la = self.la = QLabel(_( + 'You can import an HTML or DOCX file directly as an EPUB and edit it. The EPUB' + ' will be generated with minimal changes from the source, unlike doing a full' + ' conversion in calibre.')) + la.setWordWrap(True) + l.addRow(la) + + self.h1 = h1 = QHBoxLayout() + self.src = src = QLineEdit(self) + src.setPlaceholderText(_('Choose the file to import')) + h1.addWidget(src) + self.b1 = b = QToolButton(self) + b.setIcon(QIcon(I('document_open.png'))) + b.setText(_('Choose file')) + h1.addWidget(b) + l.addRow(_('Source file'), h1) + b.clicked.connect(self.choose_source) + b.setFocus(Qt.OtherFocusReason) + + self.h2 = h1 = QHBoxLayout() + self.dest = src = QLineEdit(self) + src.setPlaceholderText(_('Choose the location for the newly created EPUB')) + h1.addWidget(src) + self.b2 = b = QToolButton(self) + b.setIcon(QIcon(I('document_open.png'))) + b.setText(_('Choose file')) + h1.addWidget(b) + l.addRow(_('Destination file'), h1) + b.clicked.connect(self.choose_destination) + + l.addRow(self.bb) + + def choose_source(self): + path = choose_files(self, 'edit-book-choose-file-to-import', _('Choose file'), filters=[ + (_('HTML files'), ['htm', 'html', 'xhtml', 'xhtm']), + (_('DOCX files'), ['docx'])], select_only_single_file=True) + if path: + self.src.setText(path[0]) + self.dest.setText(self.data[1]) + + def choose_destination(self): + path = choose_save_file(self, 'edit-book-destination-for-generated-epub', _('Choose destination'), filters=[ + (_('EPUB files'), ['epub'])], all_files=False) + if path: + if not path.lower().endswith('.epub'): + path += '.epub' + self.dest.setText(path) + + def accept(self): + if not unicode(self.src.text()): + return error_dialog(self, _('Need document'), _( + 'You must specify the source file that will be imported.'), show=True) + Dialog.accept(self) + + @property + def data(self): + src = unicode(self.src.text()).strip() + dest = unicode(self.dest.text()).strip() + if not dest: + dest = src.rpartition('.')[0] + '.epub' + return src, dest +# }}} + +if __name__ == '__main__': + app = QApplication([]) + d = ImportForeign() + d.exec_() + print (d.data)