From 66d498e7b349939c8349f2d9194884d4c148be6c Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 5 Jul 2010 13:16:05 +0100 Subject: [PATCH 01/38] First iteration of automatic metadata updating --- src/calibre/devices/usbms/books.py | 13 +++++++-- src/calibre/devices/usbms/driver.py | 2 +- src/calibre/gui2/device.py | 25 ++++++++++++---- src/calibre/gui2/dialogs/config/add_save.py | 17 +++++++---- src/calibre/gui2/dialogs/config/add_save.ui | 32 ++++++++++++++++----- src/calibre/gui2/library/models.py | 2 +- src/calibre/gui2/library/views.py | 2 +- src/calibre/utils/config.py | 4 +-- 8 files changed, 71 insertions(+), 26 deletions(-) diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py index 42d0f3c863..6394626a9f 100644 --- a/src/calibre/devices/usbms/books.py +++ b/src/calibre/devices/usbms/books.py @@ -132,6 +132,8 @@ class CollectionsBookList(BookList): return True def get_collections(self, collection_attributes): + from calibre.devices.usbms.driver import debug_print + debug_print('Starting get_collections:', prefs['manage_device_metadata']) collections = {} series_categories = set([]) # This map of sets is used to avoid linear searches when testing for @@ -146,14 +148,19 @@ class CollectionsBookList(BookList): # book in all existing collections. Do not add any new ones. attrs = ['device_collections'] if getattr(book, '_new_book', False): - if prefs['preserve_user_collections']: + if prefs['manage_device_metadata'] == 'manual': # Ensure that the book is in all the book's existing # collections plus all metadata collections attrs += collection_attributes else: - # The book's existing collections are ignored. Put the book - # in collections defined by its metadata. + # For new books, both 'on_send' and 'on_connect' do the same + # thing. The book's existing collections are ignored. Put + # the book in collections defined by its metadata. attrs = collection_attributes + elif prefs['manage_device_metadata'] == 'on_connect': + # For existing books, modify the collections only if the user + # specified 'on_connect' + attrs = collection_attributes for attr in attrs: attr = attr.strip() val = getattr(book, attr, None) diff --git a/src/calibre/devices/usbms/driver.py b/src/calibre/devices/usbms/driver.py index 377ec36c16..1a088cc77c 100644 --- a/src/calibre/devices/usbms/driver.py +++ b/src/calibre/devices/usbms/driver.py @@ -58,7 +58,7 @@ class USBMS(CLI, Device): debug_print ('USBMS: Fetching list of books from device. oncard=', oncard) - dummy_bl = BookList(None, None, None) + dummy_bl = booklist_class(None, None, None) if oncard == 'carda' and not self._card_a_prefix: self.report_progress(1.0, _('Getting list of books on device...')) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 4acde6089b..d976fad128 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -33,6 +33,7 @@ from calibre.devices.apple.driver import ITUNES_ASYNC from calibre.devices.folder_device.driver import FOLDER_DEVICE from calibre.ebooks.metadata.meta import set_metadata from calibre.constants import DEBUG +from calibre.utils.config import prefs # }}} @@ -1425,19 +1426,24 @@ class DeviceMixin(object): # {{{ aus = re.sub('(?u)\W|[_]', '', aus) self.db_book_title_cache[title]['author_sort'][aus] = mi self.db_book_title_cache[title]['db_ids'][mi.application_id] = mi - self.db_book_uuid_cache[mi.uuid] = mi.application_id + self.db_book_uuid_cache[mi.uuid] = mi # Now iterate through all the books on the device, setting the # in_library field Fastest and most accurate key is the uuid. Second is # the application_id, which is really the db key, but as this can # accidentally match across libraries we also verify the title. The # db_id exists on Sony devices. Fallback is title and author match + + update_metadata = prefs['manage_device_metadata'] == 'on_connect' for booklist in booklists: for book in booklist: if getattr(book, 'uuid', None) in self.db_book_uuid_cache: + if update_metadata: + book.smart_update(self.db_book_uuid_cache[book.uuid]) book.in_library = True # ensure that the correct application_id is set - book.application_id = self.db_book_uuid_cache[book.uuid] + book.application_id = \ + self.db_book_uuid_cache[book.uuid].application_id continue book_title = book.title.lower() if book.title else '' @@ -1447,11 +1453,13 @@ class DeviceMixin(object): # {{{ if d is not None: if getattr(book, 'application_id', None) in d['db_ids']: book.in_library = True - book.smart_update(d['db_ids'][book.application_id]) + if update_metadata: + book.smart_update(d['db_ids'][book.application_id]) continue if book.db_id in d['db_ids']: book.in_library = True - book.smart_update(d['db_ids'][book.db_id]) + if update_metadata: + book.smart_update(d['db_ids'][book.db_id]) continue if book.authors: # Compare against both author and author sort, because @@ -1460,14 +1468,19 @@ class DeviceMixin(object): # {{{ book_authors = re.sub('(?u)\W|[_]', '', book_authors) if book_authors in d['authors']: book.in_library = True - book.smart_update(d['authors'][book_authors]) + if update_metadata: + book.smart_update(d['authors'][book_authors]) elif book_authors in d['author_sort']: book.in_library = True - book.smart_update(d['author_sort'][book_authors]) + if update_metadata: + book.smart_update(d['author_sort'][book_authors]) # Set author_sort if it isn't already asort = getattr(book, 'author_sort', None) if not asort and book.authors: book.author_sort = self.library_view.model().db.author_sort_from_authors(book.authors) + if update_metadata: + if self.device_manager.is_device_connected: + self.device_manager.sync_booklists(None, booklists) # }}} diff --git a/src/calibre/gui2/dialogs/config/add_save.py b/src/calibre/gui2/dialogs/config/add_save.py index b1f5621f44..8eb6cf7bd0 100644 --- a/src/calibre/gui2/dialogs/config/add_save.py +++ b/src/calibre/gui2/dialogs/config/add_save.py @@ -45,7 +45,12 @@ class AddSave(QTabWidget, Ui_TabWidget): self.metadata_box.layout().insertWidget(0, self.filename_pattern) self.opt_swap_author_names.setChecked(prefs['swap_author_names']) self.opt_add_formats_to_existing.setChecked(prefs['add_formats_to_existing']) - self.preserve_user_collections.setChecked(prefs['preserve_user_collections']) + if prefs['manage_device_metadata'] == 'manual': + self.manage_device_metadata.setCurrentIndex(0) + elif prefs['manage_device_metadata'] == 'on_send': + self.manage_device_metadata.setCurrentIndex(1) + else: + self.manage_device_metadata.setCurrentIndex(2) help = '\n'.join(textwrap.wrap(c.get_option('template').help, 75)) self.save_template.initialize('save_to_disk', opts.template, help) self.send_template.initialize('send_to_device', opts.send_template, help) @@ -72,12 +77,14 @@ class AddSave(QTabWidget, Ui_TabWidget): prefs['filename_pattern'] = pattern prefs['swap_author_names'] = bool(self.opt_swap_author_names.isChecked()) prefs['add_formats_to_existing'] = bool(self.opt_add_formats_to_existing.isChecked()) - prefs['preserve_user_collections'] = bool(self.preserve_user_collections.isChecked()) - + if self.manage_device_metadata.currentIndex() == 0: + prefs['manage_device_metadata'] = 'manual' + elif self.manage_device_metadata.currentIndex() == 1: + prefs['manage_device_metadata'] = 'on_send' + else: + prefs['manage_device_metadata'] = 'on_connect' return True - - if __name__ == '__main__': from PyQt4.Qt import QApplication app=QApplication([]) diff --git a/src/calibre/gui2/dialogs/config/add_save.ui b/src/calibre/gui2/dialogs/config/add_save.ui index 64a8137aa1..35824ef847 100644 --- a/src/calibre/gui2/dialogs/config/add_save.ui +++ b/src/calibre/gui2/dialogs/config/add_save.ui @@ -14,7 +14,7 @@ TabWidget - 0 + 2 @@ -179,16 +179,34 @@ Title match ignores leading indefinite articles ("the", "a", - - - Preserve device collections. - + + + + Manual management. Calibre updates cached metadata and adds collections, but never removes them + + + + + Calibre manages metadata when sending books. Calibre updates cached metadata, and adds/remove collections + + + + + Calibre manages metadata at device connection. Calibre updates cached metadata, and adds/removes collections + + - If checked, collections will not be deleted even if a book with changed metadata is resent and the collection is not in the book's metadata. In addition, editing collections in the device view will be enabled. If unchecked, collections will be always reflect only the metadata in the calibre library. + Choose 'Manual Management', to have Calibre update the metadata cache (not the book) and add collections when a book is sent. With this option, calibre will never remove a collection. Choose 'Calibre manages metadata when sending books' to have Calibre update the metadata cache and add/remove collections when you send a book to the device. Choose 'Calibre manages metadata when device is connected' to have Calibre update the metadata cache and add/remove collections when you connect the device. + + + Qt::PlainText + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true @@ -198,7 +216,7 @@ Title match ignores leading indefinite articles ("the", "a", - + diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 7ffbc42f02..147aa1188d 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -939,7 +939,7 @@ class DeviceBooksModel(BooksModel): # {{{ (cname == 'collections' and \ callable(getattr(self.db, 'supports_collections', None)) and \ self.db.supports_collections() and \ - prefs['preserve_user_collections']): + prefs['manage_device_metadata']=='manual'): flags |= Qt.ItemIsEditable return flags diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index 9d85dce075..c6c32f86f7 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -503,7 +503,7 @@ class DeviceBooksView(BooksView): # {{{ self.edit_collections_menu.setVisible( callable(getattr(self._model.db, 'supports_collections', None)) and \ self._model.db.supports_collections() and \ - prefs['preserve_user_collections']) + prefs['manage_device_metadata'] == 'manual') self.context_menu.popup(event.globalPos()) event.accept() diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py index f24a6d2e30..5c4bd55644 100644 --- a/src/calibre/utils/config.py +++ b/src/calibre/utils/config.py @@ -698,8 +698,8 @@ def _prefs(): # calibre server can execute searches c.add_opt('saved_searches', default={}, help=_('List of named saved searches')) c.add_opt('user_categories', default={}, help=_('User-created tag browser categories')) - c.add_opt('preserve_user_collections', default=True, - help=_('Preserve all collections even if not in library metadata.')) + c.add_opt('manage_device_metadata', default='manual', + help=_('How and when calibre updates metadata on the device.')) c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.') return c From 25905711fb0fea3454c0a6dd97422c5d2cc2fbfe Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 5 Jul 2010 14:48:51 +0100 Subject: [PATCH 02/38] 1) More changes for connect-time syncing. 2) some changes to the Boox driver --- src/calibre/devices/hanlin/driver.py | 17 ++++++++++------- src/calibre/devices/usbms/driver.py | 4 +++- src/calibre/gui2/dialogs/config/add_save.ui | 4 ++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/calibre/devices/hanlin/driver.py b/src/calibre/devices/hanlin/driver.py index f19135a7e7..3db55d05c5 100644 --- a/src/calibre/devices/hanlin/driver.py +++ b/src/calibre/devices/hanlin/driver.py @@ -119,22 +119,25 @@ class BOOX(HANLINV3): MAIN_MEMORY_VOLUME_LABEL = 'BOOX Internal Memory' STORAGE_CARD_VOLUME_LABEL = 'BOOX Storage Card' - EBOOK_DIR_MAIN = ['MyBooks'] - EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to ' - 'send e-books to on the device. The first one that exists will ' - 'be used.') + EBOOK_DIR_MAIN = ['MyBooks', 'MyBooks'] + EXTRA_CUSTOMIZATION_MESSAGE = _('Directories to send e-books to on the device. ' + 'The first directory is used for main memory. ' + 'The second is used for cards.') EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(EBOOK_DIR_MAIN) - # EBOOK_DIR_CARD_A = 'MyBooks' ## Am quite sure we need this. - def post_open_callback(self): opts = self.settings() dirs = opts.extra_customization if not dirs: dirs = self.EBOOK_DIR_MAIN else: + # will have at least one item ... dirs = [x.strip() for x in dirs.split(',')] - self.EBOOK_DIR_MAIN = dirs + self.EBOOK_DIR_MAIN = dirs[0] + if len(dirs) > 1: + self.EBOOK_DIR_CARD_A = self.EBOOK_DIR_CARD_B = dirs[1] + else: + self.EBOOK_DIR_CARD_A = self.EBOOK_DIR_CARD_B = dirs[0] def windows_sort_drives(self, drives): return drives diff --git a/src/calibre/devices/usbms/driver.py b/src/calibre/devices/usbms/driver.py index 1a088cc77c..73a329be58 100644 --- a/src/calibre/devices/usbms/driver.py +++ b/src/calibre/devices/usbms/driver.py @@ -58,7 +58,7 @@ class USBMS(CLI, Device): debug_print ('USBMS: Fetching list of books from device. oncard=', oncard) - dummy_bl = booklist_class(None, None, None) + dummy_bl = self.booklist_class(None, None, None) if oncard == 'carda' and not self._card_a_prefix: self.report_progress(1.0, _('Getting list of books on device...')) @@ -78,6 +78,8 @@ class USBMS(CLI, Device): self.EBOOK_DIR_CARD_B if oncard == 'cardb' else \ self.get_main_ebook_dir() + debug_print ('USBMS: dirs are:', prefix, ebook_dirs) + # get the metadata cache bl = self.booklist_class(oncard, prefix, self.settings) need_sync = self.parse_metadata_cache(bl, prefix, self.METADATA_CACHE) diff --git a/src/calibre/gui2/dialogs/config/add_save.ui b/src/calibre/gui2/dialogs/config/add_save.ui index 35824ef847..9ded5d91c2 100644 --- a/src/calibre/gui2/dialogs/config/add_save.ui +++ b/src/calibre/gui2/dialogs/config/add_save.ui @@ -6,7 +6,7 @@ 0 0 - 588 + 599 516 @@ -200,7 +200,7 @@ Title match ignores leading indefinite articles ("the", "a", - Choose 'Manual Management', to have Calibre update the metadata cache (not the book) and add collections when a book is sent. With this option, calibre will never remove a collection. Choose 'Calibre manages metadata when sending books' to have Calibre update the metadata cache and add/remove collections when you send a book to the device. Choose 'Calibre manages metadata when device is connected' to have Calibre update the metadata cache and add/remove collections when you connect the device. + Choose 'Manual Management', to have Calibre update the metadata cache (not the book) and add collections when a book is sent. With this option, calibre will never remove a collection. Choose 'Calibre manages metadata when sending books' to have Calibre update the metadata cache and add/remove collections for a book when it is sent to the device. Choose 'Calibre manages metadata when device is connected' to have Calibre update the metadata cache and add/remove collections when calibre connects to the device and when you send a book. Qt::PlainText From 0b4e09180dfd8d443a7fa1a9ebbae4806ca834c2 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 8 Jul 2010 18:30:19 +0100 Subject: [PATCH 03/38] Revert boox driver --- src/calibre/devices/hanlin/driver.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/calibre/devices/hanlin/driver.py b/src/calibre/devices/hanlin/driver.py index 3db55d05c5..f19135a7e7 100644 --- a/src/calibre/devices/hanlin/driver.py +++ b/src/calibre/devices/hanlin/driver.py @@ -119,25 +119,22 @@ class BOOX(HANLINV3): MAIN_MEMORY_VOLUME_LABEL = 'BOOX Internal Memory' STORAGE_CARD_VOLUME_LABEL = 'BOOX Storage Card' - EBOOK_DIR_MAIN = ['MyBooks', 'MyBooks'] - EXTRA_CUSTOMIZATION_MESSAGE = _('Directories to send e-books to on the device. ' - 'The first directory is used for main memory. ' - 'The second is used for cards.') + EBOOK_DIR_MAIN = ['MyBooks'] + EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to ' + 'send e-books to on the device. The first one that exists will ' + 'be used.') EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(EBOOK_DIR_MAIN) + # EBOOK_DIR_CARD_A = 'MyBooks' ## Am quite sure we need this. + def post_open_callback(self): opts = self.settings() dirs = opts.extra_customization if not dirs: dirs = self.EBOOK_DIR_MAIN else: - # will have at least one item ... dirs = [x.strip() for x in dirs.split(',')] - self.EBOOK_DIR_MAIN = dirs[0] - if len(dirs) > 1: - self.EBOOK_DIR_CARD_A = self.EBOOK_DIR_CARD_B = dirs[1] - else: - self.EBOOK_DIR_CARD_A = self.EBOOK_DIR_CARD_B = dirs[0] + self.EBOOK_DIR_MAIN = dirs def windows_sort_drives(self, drives): return drives From 0a7171d7b7bfef8ba7657569dc840bf96ff7411b Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 10 Jul 2010 06:40:53 +0100 Subject: [PATCH 04/38] Change search restriction box to use grey help text. Remove label. --- src/calibre/gui2/main.ui | 17 +++---- src/calibre/gui2/search_restriction_mixin.py | 7 +-- src/calibre/gui2/widgets.py | 52 ++++++++++++++++++++ 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/calibre/gui2/main.ui b/src/calibre/gui2/main.ui index d89a451cda..0d937527f6 100644 --- a/src/calibre/gui2/main.ui +++ b/src/calibre/gui2/main.ui @@ -123,17 +123,7 @@ 0 - - - &Restrict to: - - - search_restriction - - - - - + 150 @@ -543,6 +533,11 @@ QComboBox
calibre.gui2.search_box
+ + ComboBoxWithHelp + QComboBox +
calibre.gui2.widgets
+
ThrobbingButton QToolButton diff --git a/src/calibre/gui2/search_restriction_mixin.py b/src/calibre/gui2/search_restriction_mixin.py index 3a71fa3de0..6f31f65c13 100644 --- a/src/calibre/gui2/search_restriction_mixin.py +++ b/src/calibre/gui2/search_restriction_mixin.py @@ -7,7 +7,8 @@ Created on 10 Jun 2010 class SearchRestrictionMixin(object): def __init__(self): - self.search_restriction.activated[str].connect(self.apply_search_restriction) + self.search_restriction.initialize(help_text=_('Restrict To')) + self.search_restriction.activated[int].connect(self.apply_search_restriction) self.library_view.model().count_changed_signal.connect(self.restriction_count_changed) self.search_restriction.setSizeAdjustPolicy(self.search_restriction.AdjustToMinimumContentsLengthWithIcon) self.search_restriction.setMinimumContentsLength(10) @@ -27,8 +28,8 @@ class SearchRestrictionMixin(object): if self.restriction_in_effect: self.set_number_of_books_shown() - def apply_search_restriction(self, r): - r = unicode(r) + def apply_search_restriction(self, i): + r = unicode(self.search_restriction.currentText()) if r is not None and r != '': self.restriction_in_effect = True restriction = 'search:"%s"'%(r) diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index d94d8e7292..63be2b19ec 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -731,6 +731,58 @@ class HistoryLineEdit(QComboBox): def text(self): return self.currentText() +class ComboBoxWithHelp(QComboBox): + ''' + A combobox where item 0 is help text. CurrentText will return '' for item 0. + Be sure to always fetch the text with currentText. Don't use the signals + that pass a string, because they will not correct the text. + ''' + def __init__(self, parent=None): + QComboBox.__init__(self, parent) + self.normal_background = 'rgb(255, 255, 255, 0%)' + self.connect(self, SIGNAL('currentIndexChanged(int)'), self.index_changed) + self.help_text = '' + self.state_set = False + + def initialize(self, help_text=_('Search')): + self.help_text = help_text + self.set_state() + + def set_state(self): + if not self.state_set: + if self.currentIndex() == 0: + self.setItemText(0, self.help_text) + self.setStyleSheet( + 'QComboBox { color: gray; background-color: %s; }' % + self.normal_background) + else: + self.setItemText(0, '') + self.setStyleSheet( + 'QComboBox { color: black; background-color: %s; }' % + self.normal_background) + + def index_changed(self, index): + self.state_set = False + self.set_state() + + def currentText(self): + if self.currentIndex() == 0: + return '' + return QComboBox.currentText(self) + + def itemText(self, idx): + if idx == 0: + return '' + return QComboBox.itemText(self, idx) + + def showPopup(self): + self.setItemText(0, '') + QComboBox.showPopup(self) + + def hidePopup(self): + QComboBox.hidePopup(self) + self.set_state() + class PythonHighlighter(QSyntaxHighlighter): Rules = [] From 3bf6888b3423f8d2479ce59673a0a596a63e9f2c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 10 Jul 2010 06:37:49 -0600 Subject: [PATCH 05/38] Fix #6136 (epub-fix fails on ePub check) --- src/calibre/ebooks/epub/fix/container.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/calibre/ebooks/epub/fix/container.py b/src/calibre/ebooks/epub/fix/container.py index 7a7c17427a..b9af66d708 100644 --- a/src/calibre/ebooks/epub/fix/container.py +++ b/src/calibre/ebooks/epub/fix/container.py @@ -5,7 +5,7 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import os, posixpath, urllib, sys +import os, posixpath, urllib, sys, re from lxml import etree @@ -160,8 +160,26 @@ class Container(object): mt = mimetype.lower() if mt.endswith('+xml'): parser = etree.XMLParser(no_network=True, huge_tree=not iswindows) - return etree.fromstring(xml_to_unicode(raw, - strip_encoding_pats=True, assume_utf8=True)[0], parser=parser) + raw = xml_to_unicode(raw, + strip_encoding_pats=True, assume_utf8=True, + resolve_entities=True)[0].strip() + idx = raw.find(' -1: + pre = raw[:idx] + raw = raw[idx:] + if ']+)', pre): + val = match.group(2) + if val.startswith('"') and val.endswith('"'): + val = val[1:-1] + user_entities[match.group(1)] = val + if user_entities: + pat = re.compile(r'&(%s);'%('|'.join(user_entities.keys()))) + raw = pat.sub(lambda m:user_entities[m.group(1)], raw) + return etree.fromstring(raw, parser=parser) return raw def write(self, path): From 336294fe358e23794a35c7122fd635aba077c9ad Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 10 Jul 2010 14:07:00 +0100 Subject: [PATCH 06/38] Recover from UI changes --- src/calibre/gui2/layout.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index f3b650f531..7b70224872 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -12,6 +12,7 @@ from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, QVariant, \ from calibre.constants import __appname__, filesystem_encoding from calibre.gui2.search_box import SearchBox2, SavedSearchBox +from calibre.gui2.widgets import ComboBoxWithHelp from calibre.gui2.throbber import ThrobbingButton from calibre.gui2 import NONE from calibre import human_readable @@ -280,12 +281,7 @@ class SearchBar(QWidget): # {{{ self._layout = l = QHBoxLayout() self.setLayout(self._layout) - self.restriction_label = QLabel(_("&Restrict to:")) - l.addWidget(self.restriction_label) - self.restriction_label.setSizePolicy(QSizePolicy.Minimum, - QSizePolicy.Minimum) - - x = QComboBox(self) + x = ComboBoxWithHelp(self) x.setMaximumSize(QSize(150, 16777215)) x.setObjectName("search_restriction") x.setToolTip(_("Books display will be restricted to those matching the selected saved search")) @@ -344,7 +340,6 @@ class SearchBar(QWidget): # {{{ x.setToolTip(_("Delete current saved search")) self.label.setBuddy(parent.search) - self.restriction_label.setBuddy(parent.search_restriction) # }}} From f9896d01f31a638dc87b884600b90fd7cf4e147e Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 10 Jul 2010 15:40:38 +0100 Subject: [PATCH 07/38] Put GPL line back in (don't know how it disappeared) --- src/calibre/gui2/widgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index e3874fdc3a..97758482fc 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -1,3 +1,4 @@ +__license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' ''' Miscellaneous widgets used in the GUI From 4979544b20f90171e461bdff2151375a5228dd46 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 10 Jul 2010 09:11:21 -0600 Subject: [PATCH 08/38] ... --- src/calibre/gui2/dialogs/config/add_save.ui | 67 ++++++++++++++------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/src/calibre/gui2/dialogs/config/add_save.ui b/src/calibre/gui2/dialogs/config/add_save.ui index 80df2d80ca..c8ee0419a9 100644 --- a/src/calibre/gui2/dialogs/config/add_save.ui +++ b/src/calibre/gui2/dialogs/config/add_save.ui @@ -6,7 +6,7 @@ 0 0 - 953 + 671 516
@@ -178,8 +178,14 @@ Title match ignores leading indefinite articles ("the", "a", Sending to &device - + + + + 0 + 0 + + Metadata &management: @@ -188,6 +194,44 @@ Title match ignores leading indefinite articles ("the", "a", + + + + + 0 + 0 + + + + + Manual management + + + + + Only on send + + + + + Automatic management + + + + + + + + Qt::Horizontal + + + + 313 + 20 + + + + @@ -220,25 +264,6 @@ Title match ignores leading indefinite articles ("the", "a", - - - - - Manual management - - - - - Only on send - - - - - Automatic management - - - -
From bc1dfb3210b5fc25da3671c3c13f5285132cf25d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 10 Jul 2010 09:28:44 -0600 Subject: [PATCH 09/38] Upgrade version of Qt in windows build to 4.6.3 --- setup/installer/windows/freeze.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/installer/windows/freeze.py b/setup/installer/windows/freeze.py index 0b04cc11cc..29809907ab 100644 --- a/setup/installer/windows/freeze.py +++ b/setup/installer/windows/freeze.py @@ -13,7 +13,7 @@ from setup import Command, modules, functions, basenames, __version__, \ from setup.build_environment import msvc, MT, RC from setup.installer.windows.wix import WixMixIn -QT_DIR = 'C:\\Qt\\4.6.0' +QT_DIR = 'C:\\Qt\\4.6.3' QT_DLLS = ['Core', 'Gui', 'Network', 'Svg', 'WebKit', 'Xml', 'XmlPatterns'] LIBUSB_DIR = 'C:\\libusb' LIBUNRAR = 'C:\\Program Files\\UnrarDLL\\unrar.dll' From 10b1671d141653c1aafdeca4c2ea65c995a816da Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 10 Jul 2010 10:06:23 -0600 Subject: [PATCH 10/38] ... --- src/calibre/translations/calibre.pot | 834 ++++++++++++++------------- 1 file changed, 444 insertions(+), 390 deletions(-) diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index 02db8dde80..16a7eae7ec 100644 --- a/src/calibre/translations/calibre.pot +++ b/src/calibre/translations/calibre.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: calibre 0.7.8\n" -"POT-Creation-Date: 2010-07-09 15:14+MDT\n" -"PO-Revision-Date: 2010-07-09 15:14+MDT\n" +"POT-Creation-Date: 2010-07-10 10:05+MDT\n" +"PO-Revision-Date: 2010-07-10 10:05+MDT\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\n" "MIME-Version: 1.0\n" @@ -26,7 +26,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/devices/nook/driver.py:71 #: /home/kovid/work/calibre/src/calibre/devices/prs500/books.py:267 #: /home/kovid/work/calibre/src/calibre/devices/prs505/sony_cache.py:492 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:396 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:398 #: /home/kovid/work/calibre/src/calibre/ebooks/chm/input.py:97 #: /home/kovid/work/calibre/src/calibre/ebooks/chm/input.py:100 #: /home/kovid/work/calibre/src/calibre/ebooks/chm/metadata.py:56 @@ -110,10 +110,10 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:110 #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:135 #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:137 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:898 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:907 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1190 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1193 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:899 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:908 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1191 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1194 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf.py:47 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:120 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:155 @@ -629,30 +629,30 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:64 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:67 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:70 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:130 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:137 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:160 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:132 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:139 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:162 msgid "Getting list of books on device..." msgstr "" #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:219 #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:263 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:244 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:262 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:246 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:264 msgid "Removing books from device..." msgstr "" #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:267 #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:274 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:269 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:274 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:271 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:276 msgid "Removing books from device metadata listing..." msgstr "" #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:279 #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:313 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:208 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:238 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:210 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:240 msgid "Adding books to device metadata listing..." msgstr "" @@ -813,12 +813,12 @@ msgstr "" msgid "Get device information..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:188 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:190 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:192 msgid "Transferring books to device..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:305 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:307 msgid "Sending metadata to device..." msgstr "" @@ -1301,7 +1301,7 @@ msgid "Workaround epubcheck bugs" msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/epub/fix/epubcheck.py:22 -msgid "Workarounds for bugs in the latest release of epubcheck. epubcheck reports many things as errors that are not actually errors. %prog will try to detect these and replace them with constructs that epubcheck likes. This may cause significant changes to your epub, complain to the epubcheck project." +msgid "Workarounds for bugs in the latest release of epubcheck. epubcheck reports many things as errors that are not actually errors. epub-fix will try to detect these and replace them with constructs that epubcheck likes. This may cause significant changes to your epub, complain to the epubcheck project." msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/epub/fix/main.py:19 @@ -1322,7 +1322,7 @@ msgid "Fix unmanifested files" msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/epub/fix/unmanifested.py:21 -msgid "Fix unmanifested files. %prog can either add them to the manifest or delete them as specified by the delete unmanifested option." +msgid "Fix unmanifested files. epub-fix can either add them to the manifest or delete them as specified by the delete unmanifested option." msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/epub/fix/unmanifested.py:32 @@ -2580,14 +2580,14 @@ msgid "Main memory" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions.py:519 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:444 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:453 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:445 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:454 msgid "Storage Card A" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions.py:520 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:446 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:455 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:447 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:456 msgid "Storage Card B" msgstr "" @@ -2645,7 +2645,7 @@ msgid "Failed to download metadata:" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions.py:659 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:637 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:638 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:523 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:951 #: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:53 @@ -2797,7 +2797,7 @@ msgid "The specified directory could not be processed." msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/add.py:263 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:840 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:841 msgid "No books" msgstr "" @@ -2942,12 +2942,12 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:267 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:269 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:270 -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:248 -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:253 -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:259 -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:261 -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:263 -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:265 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:132 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:137 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:143 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:145 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:147 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:149 #: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:75 #: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:80 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:186 @@ -3012,7 +3012,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/shortcuts.py:47 #: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:73 #: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:78 -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:467 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:274 msgid "None" msgstr "" @@ -3836,12 +3836,12 @@ msgid "Footer regular expression:" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:56 -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:77 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:76 msgid "Invalid regular expression" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:57 -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:78 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:77 msgid "Invalid regular expression: %s" msgstr "" @@ -4109,250 +4109,250 @@ msgstr "" msgid "tags to remove" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:48 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:49 #: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:135 msgid "No details available." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:154 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:155 msgid "Device no longer connected." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:270 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:271 msgid "Get device information" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:281 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:282 msgid "Get list of books on device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:291 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:292 msgid "Get annotations from device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:300 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:301 msgid "Send metadata to device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:305 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:306 msgid "Send collections to device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:329 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:330 msgid "Upload %d books to device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:344 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:345 msgid "Delete books from device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:361 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:362 msgid "Download books from device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:371 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:372 msgid "View book on device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:407 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:408 msgid "Set default send to device action" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:413 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:420 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:422 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:424 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:414 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:421 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:423 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:425 msgid "Email to" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:424 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:425 msgid " and delete from library" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:433 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:434 msgid "Send to main memory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:435 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:436 msgid "Send to storage card A" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:437 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:438 msgid "Send to storage card B" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:442 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:451 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:443 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:452 msgid "Main Memory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:469 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:470 msgid "Send and delete from library" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:470 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:471 msgid "Send specific format" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:509 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:510 msgid "Connect to folder" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:515 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:516 msgid "Connect to iTunes" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:520 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:521 msgid "Eject device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:528 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:529 msgid "Fetch annotations (experimental)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:638 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:639 msgid "Error communicating with device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:659 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:660 msgid "Select folder to open as device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:704 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:705 msgid "Failed" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:710 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:711 msgid "Error talking to device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:711 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:712 msgid "There was a temporary error talking to the device. Please unplug and reconnect the device and or reboot." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:753 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:754 msgid "Device: " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:755 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:756 msgid " detected." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:841 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:842 msgid "selected to send" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:846 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:847 msgid "Choose format to send to device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:855 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:856 msgid "No device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:856 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:857 msgid "Cannot send: No device is connected" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:859 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:863 -msgid "No card" -msgstr "" - #: /home/kovid/work/calibre/src/calibre/gui2/device.py:860 #: /home/kovid/work/calibre/src/calibre/gui2/device.py:864 +msgid "No card" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:861 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:865 msgid "Cannot send: Device has no storage card" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:905 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:906 msgid "E-book:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:908 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:909 msgid "Attached, you will find the e-book" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:909 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:910 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:181 msgid "by" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:910 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:911 msgid "in the %s format." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:923 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:924 msgid "Sending email to" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:953 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:961 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1053 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1115 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1234 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1242 -msgid "No suitable formats" -msgstr "" - #: /home/kovid/work/calibre/src/calibre/gui2/device.py:954 -msgid "Auto convert the following books before sending via email?" -msgstr "" - #: /home/kovid/work/calibre/src/calibre/gui2/device.py:962 -msgid "Could not email the following books as no suitable formats were found:" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:980 -msgid "Failed to email books" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:981 -msgid "Failed to email the following books:" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:985 -msgid "Sent by email:" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1012 -msgid "News:" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1013 -msgid "Attached is the" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1024 -msgid "Sent news to" -msgstr "" - #: /home/kovid/work/calibre/src/calibre/gui2/device.py:1054 #: /home/kovid/work/calibre/src/calibre/gui2/device.py:1116 #: /home/kovid/work/calibre/src/calibre/gui2/device.py:1235 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1243 +msgid "No suitable formats" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:955 +msgid "Auto convert the following books before sending via email?" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:963 +msgid "Could not email the following books as no suitable formats were found:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:981 +msgid "Failed to email books" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:982 +msgid "Failed to email the following books:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:986 +msgid "Sent by email:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1013 +msgid "News:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1014 +msgid "Attached is the" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1025 +msgid "Sent news to" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1055 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1117 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1236 msgid "Auto convert the following books before uploading to the device?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1084 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1085 msgid "Sending catalogs to device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1148 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1149 msgid "Sending news to device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1201 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1202 msgid "Sending books to device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1243 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1244 msgid "Could not upload the following books to the device, as no suitable formats were found. Convert the book(s) to a format supported by your device first." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1304 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1305 msgid "No space on device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1305 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1306 msgid "

Cannot upload books to device there is no more free space available " msgstr "" @@ -4613,7 +4613,7 @@ msgid "Access log:" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:794 -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:332 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:331 msgid "Failed to start content server" msgstr "" @@ -4671,27 +4671,27 @@ msgstr "" msgid "The following books had formats listed in the database that are not actually available. The entries for the formats have been removed. You should check them manually. This can happen if you manipulate the files in the library folder directly." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:113 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:133 msgid "TabWidget" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:114 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:134 msgid "Here you can control how calibre will read metadata from the files you add to it. calibre can either read metadata from the contents of the file, or from the filename." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:115 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:135 msgid "Read metadata only from &file name" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:116 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:136 msgid "Swap the firstname and lastname of the author. This affects only metadata read from file names." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:117 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:137 msgid "&Swap author firstname and lastname" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:118 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:138 msgid "" "If an existing book with a similar title and author is found that does not have the format being added, the format is added\n" "to the existing book, instead of creating a new entry. If the existing book already has the format, then it is silently ignored.\n" @@ -4699,81 +4699,93 @@ msgid "" "Title match ignores leading indefinite articles (\"the\", \"a\", \"an\"), punctuation, case, etc. Author match is exact." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:122 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:142 msgid "If books with similar titles and authors found, &merge the new files automatically" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:123 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:143 msgid "&Configure metadata from file name" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:124 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:144 msgid "&Adding books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:125 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:145 msgid "Here you can control how calibre will save your books when you click the Save to Disk button:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:126 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:146 msgid "Save &cover separately" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:127 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:147 msgid "Update &metadata in saved copies" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:128 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:148 msgid "Save metadata in &OPF file" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:129 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:149 msgid "Convert non-English characters to &English equivalents" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:130 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:150 msgid "Format &dates as:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:131 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:151 msgid "File &formats to save:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:132 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:152 msgid "Replace space with &underscores" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:133 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:153 msgid "Change paths to &lowercase" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:134 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:154 msgid "&Saving books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:135 -msgid "Preserve device collections." +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:155 +msgid "Metadata &management:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:136 -msgid "If checked, collections will not be deleted even if a book with changed metadata is resent and the collection is not in the book's metadata. In addition, editing collections in the device view will be enabled. If unchecked, collections will be always reflect only the metadata in the calibre library." +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:156 +msgid "Manual management" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:137 -msgid " " +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:157 +msgid "Only on send" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:138 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:158 +msgid "Automatic management" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:159 +msgid "" +"

  • Manual Management: Calibre updates the metadata and adds collections only when a book is sent. With this option, calibre will never remove a collection.
  • \n" +"
  • Only on send: Calibre updates metadata and adds/removes collections for a book only when it is sent to the device.
  • \n" +"
  • Automatic management: Calibre automatically keeps metadata on the device in sync with the calibre library, on every connect
  • " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:162 msgid "Here you can control how calibre will save your books when you click the Send to Device button. This setting can be overriden for individual devices by customizing the device interface plugins in Preferences->Plugins" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:139 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:163 msgid "Sending to &device" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:554 #: /home/kovid/work/calibre/src/calibre/gui2/init.py:168 -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:290 +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:425 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:174 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:201 msgid "Preferences" msgstr "" @@ -6410,11 +6422,11 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:126 #: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:129 #: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:132 -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:84 -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:88 -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:93 -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:98 -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:100 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:83 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:87 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:92 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:97 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:99 msgid "No match" msgstr "" @@ -6503,7 +6515,8 @@ msgid "Add Empty book. (Book entry with no formats)" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/init.py:103 -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:276 +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:409 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:160 msgid "Save to disk" msgstr "" @@ -6512,17 +6525,18 @@ msgid "Save to disk in a single directory" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/init.py:107 -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:394 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:393 msgid "Save only %s format to disk" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/init.py:111 -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:397 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:396 msgid "Save only %s format to disk in a single directory" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/init.py:120 -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:282 +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:412 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:166 msgid "View" msgstr "" @@ -6566,46 +6580,46 @@ msgstr "" msgid "Run welcome wizard" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:205 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:191 msgid "Similar books..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:237 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:223 msgid "Add books to library" msgstr "" +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:225 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:232 #: /home/kovid/work/calibre/src/calibre/gui2/init.py:239 -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:246 -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:253 msgid "Manage collections" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:333 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:319 msgid "Cover Browser" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:351 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:337 msgid "Tag Browser" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:372 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:358 msgid "version" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:373 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:359 msgid "created by Kovid Goyal" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:391 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:377 msgid "Connected " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:400 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:386 msgid "Update found" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:445 -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:454 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:430 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:439 msgid "Book Details" msgstr "" @@ -6668,6 +6682,215 @@ msgstr "" msgid " - Jobs" msgstr "" +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:64 +msgid "" +"Library\n" +"%d books" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:65 +msgid "" +"Reader\n" +"%s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:66 +msgid "" +"Card A\n" +"%s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:67 +msgid "" +"Card B\n" +"%s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:71 +msgid "Click to see the books available on your computer" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:74 +msgid "Click to see the books in the main memory of your reader" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:75 +msgid "Click to see the books on storage card A in your reader" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:76 +msgid "Click to see the books on storage card B in your reader" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:84 +msgid "Books located at" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:100 +msgid "free" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:287 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:134 +msgid "Books display will be restricted to those matching the selected saved search" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:300 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:136 +msgid "Advanced search" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:309 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:140 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:141 +msgid "

    Search the list of books by title, author, publisher, tags, comments, etc.

    Words separated by spaces are ANDed" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:316 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:142 +msgid "Reset Quick Search" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:328 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:144 +msgid "Copy current search text (instead of search name)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:334 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:146 +msgid "Save current search under the name shown in the box" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:340 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:148 +msgid "Delete current saved search" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:404 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:150 +msgid "Add books" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:404 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:151 +msgid "A" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:405 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:152 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:153 +msgid "Remove books" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:405 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:154 +msgid "Del" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:406 +msgid "Edit meta info" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:406 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:156 +msgid "E" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:407 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:157 +msgid "Merge book records" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:407 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:158 +msgid "M" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:408 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:159 +msgid "Send to device" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:409 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:161 +msgid "S" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:410 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:162 +msgid "Fetch news" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:410 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:163 +msgid "F" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:411 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:164 +msgid "Convert books" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:411 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:165 +msgid "C" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:412 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:167 +msgid "V" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:413 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:168 +msgid "Open containing folder" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:415 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:169 +msgid "Show book details" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:417 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:170 +msgid "Books by same author" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:419 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:171 +msgid "Books in this series" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:421 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:172 +msgid "Books by this publisher" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:423 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:173 +msgid "Books with the same tags" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:425 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:176 +msgid "Ctrl+P" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:426 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:177 +msgid "Help" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:426 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:178 +msgid "Browse the calibre User Manual" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:426 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:179 +msgid "F1" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:284 msgid "N" msgstr "" @@ -6949,160 +7172,34 @@ msgstr "" msgid "try deleting the file" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:247 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:131 msgid "calibre" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:249 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:133 msgid "&Restrict to:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:250 -msgid "Books display will be restricted to those matching the selected saved search" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:251 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:135 msgid "set in ui.py" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:252 -msgid "Advanced search" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:254 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:138 msgid "Alt+S" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:255 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:139 msgid "&Search:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:256 -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:257 -msgid "

    Search the list of books by title, author, publisher, tags, comments, etc.

    Words separated by spaces are ANDed" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:258 -msgid "Reset Quick Search" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:260 -msgid "Copy current search text (instead of search name)" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:262 -msgid "Save current search under the name shown in the box" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:264 -msgid "Delete current saved search" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:266 -msgid "Add books" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:267 -msgid "A" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:268 -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:269 -msgid "Remove books" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:270 -msgid "Del" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:271 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:155 msgid "Edit meta information" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:272 -msgid "E" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:273 -msgid "Merge book records" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:274 -msgid "M" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:275 -msgid "Send to device" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:277 -msgid "S" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:278 -msgid "Fetch news" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:279 -msgid "F" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:280 -msgid "Convert E-books" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:281 -msgid "C" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:283 -msgid "V" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:284 -msgid "Open containing folder" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:285 -msgid "Show book details" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:286 -msgid "Books by same author" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:287 -msgid "Books in this series" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:288 -msgid "Books by this publisher" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:289 -msgid "Books with the same tags" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:291 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:175 msgid "Configure calibre" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:292 -msgid "Ctrl+P" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:293 -msgid "Help" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:294 -msgid "Browse the calibre User Manual" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:295 -msgid "F1" -msgstr "" - #: /home/kovid/work/calibre/src/calibre/gui2/main_window.py:20 msgid "Redirect console output to a dialog window (both stdout and stderr). Useful on windows where GUI apps do not have a output streams." msgstr "" @@ -7129,6 +7226,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:93 #: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:262 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:553 msgid "Search" msgstr "" @@ -7148,15 +7246,19 @@ msgstr "" msgid "Choose saved search or enter name for new saved search" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/search_restriction_mixin.py:47 +#: /home/kovid/work/calibre/src/calibre/gui2/search_restriction_mixin.py:10 +msgid "Restrict to" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/search_restriction_mixin.py:48 msgid "({0} of {1})" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/search_restriction_mixin.py:53 +#: /home/kovid/work/calibre/src/calibre/gui2/search_restriction_mixin.py:54 msgid "(all books)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/search_restriction_mixin.py:55 +#: /home/kovid/work/calibre/src/calibre/gui2/search_restriction_mixin.py:56 msgid "({0} of all)" msgstr "" @@ -7358,64 +7460,64 @@ msgstr "" msgid "Calibre Quick Start Guide" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:376 -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:381 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:375 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:380 msgid "Cannot configure" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:377 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:376 msgid "Cannot configure while there are running jobs." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:382 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:381 msgid "Cannot configure before calibre is restarted." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:425 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:424 msgid "No detailed info available" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:426 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:425 msgid "No detailed information is available for books on the device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:478 -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:506 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:477 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:505 msgid "Conversion Error" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:479 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:478 msgid "

    Could not convert: %s

    It is a DRMed book. You must first remove the DRM using third party tools." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:492 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:491 msgid "Recipe Disabled" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:507 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:506 msgid "Failed" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:547 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:546 msgid "is the result of the efforts of many volunteers from all over the world. If you find it useful, please consider donating to support its development. Your donation helps keep calibre development going." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:573 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:572 msgid "There are active jobs. Are you sure you want to quit?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:576 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:575 msgid "" " is communicating with the device!
    \n" " Quitting may cause corruption on the device.
    \n" " Are you sure you want to quit?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:580 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:579 msgid "WARNING: Active jobs" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:633 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:632 msgid "will keep running in the system tray. To close it, choose Quit in the context menu of the system tray." msgstr "" @@ -7883,91 +7985,43 @@ msgstr "" msgid "Print eBook" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:239 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:238 msgid "Copy Image" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:240 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:239 msgid "Paste Image" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:270 -msgid "" -"Library\n" -"%d books" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:271 -msgid "" -"Reader\n" -"%s" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:272 -msgid "" -"Card A\n" -"%s" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:273 -msgid "" -"Card B\n" -"%s" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:277 -msgid "Click to see the books available on your computer" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:280 -msgid "Click to see the books in the main memory of your reader" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:281 -msgid "Click to see the books on storage card A in your reader" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:282 -msgid "Click to see the books on storage card B in your reader" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:290 -msgid "Books located at" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:306 -msgid "free" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:557 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:364 msgid "Change Case" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:558 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:365 msgid "Upper Case" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:559 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:366 msgid "Lower Case" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:560 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:367 msgid "Swap Case" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:561 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:368 msgid "Title Case" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:983 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:837 msgid "Drag to resize" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:1015 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:869 msgid "Show" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:1022 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:876 msgid "Hide" msgstr "" @@ -8949,7 +9003,7 @@ msgid "User-created tag browser categories" msgstr "" #: /home/kovid/work/calibre/src/calibre/utils/config.py:702 -msgid "Preserve all collections even if not in library metadata." +msgid "How and when calibre updates metadata on the device." msgstr "" #: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:43 From 1d1a19f4d0914a6a4fafe68fcd35ef55855ac28e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 10 Jul 2010 16:25:00 -0600 Subject: [PATCH 11/38] Upgrade to Qt 4.6.3 in linux binary builds --- setup/installer/linux/freeze.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/setup/installer/linux/freeze.py b/setup/installer/linux/freeze.py index 8c56ed4fb7..382c7ffeee 100644 --- a/setup/installer/linux/freeze.py +++ b/setup/installer/linux/freeze.py @@ -40,6 +40,7 @@ class LinuxFreeze(Command): '/usr/bin/pdftohtml', '/usr/lib/libwmflite-0.2.so.7', '/usr/lib/liblcms.so.1', + '/usr/lib/liblcms2.so.2', '/usr/lib/libstlport.so.5.1', '/tmp/calibre-mount-helper', '/usr/lib/libunrar.so', @@ -50,10 +51,9 @@ class LinuxFreeze(Command): '/usr/lib/libpodofo.so.0.8.1', '/lib/libz.so.1', '/lib/libuuid.so.1', - '/usr/lib/libtiff.so.3', + '/usr/lib/libtiff.so.5', '/lib/libbz2.so.1', - '/usr/lib/libpoppler.so.5', - '/usr/lib/libpoppler-qt4.so.3', + '/usr/lib/libpoppler.so.6', '/usr/lib/libxml2.so.2', '/usr/lib/libopenjpeg.so.2', '/usr/lib/libxslt.so.1', @@ -62,10 +62,10 @@ class LinuxFreeze(Command): '/usr/lib/libgthread-2.0.so.0', stdcpp, ffi, - '/usr/lib/libpng12.so.0', + '/usr/lib/libpng14.so.14', '/usr/lib/libexslt.so.0', - '/usr/lib/libMagickWand.so.2', - '/usr/lib/libMagickCore.so.2', + '/usr/lib/libMagickWand.so.3', + '/usr/lib/libMagickCore.so.3', '/usr/lib/libgcrypt.so.11', '/usr/lib/libgpg-error.so.0', '/usr/lib/libphonon.so.4', From 62a5b4d459a9dc9dbb24c99e411376c43cb79f6a Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 11 Jul 2010 10:04:49 +0100 Subject: [PATCH 12/38] Fixes to work around Qt strangeness with date editing --- src/calibre/gui2/custom_column_widgets.py | 9 +++++++-- src/calibre/gui2/library/delegates.py | 8 ++++---- src/calibre/library/caches.py | 4 ++-- src/calibre/utils/date.py | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index 2a9c81e8ee..58c2f15452 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -132,9 +132,11 @@ class DateEdit(QDateEdit): def focusInEvent(self, x): self.setSpecialValueText('') + QDateEdit.focusInEvent(self, x) def focusOutEvent(self, x): self.setSpecialValueText(_('Undefined')) + QDateEdit.focusOutEvent(self, x) class DateTime(Base): @@ -142,7 +144,10 @@ class DateTime(Base): self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), DateEdit(parent)] w = self.widgets[1] - w.setDisplayFormat('dd MMM yyyy') + format = self.col_metadata['display'].get('date_format','') + if not format: + format = 'dd MMM yyyy' + w.setDisplayFormat(format) w.setCalendarPopup(True) w.setMinimumDate(UNDEFINED_QDATE) w.setSpecialValueText(_('Undefined')) @@ -156,7 +161,7 @@ class DateTime(Base): def getter(self): val = self.widgets[1].date() - if val == UNDEFINED_QDATE: + if val <= UNDEFINED_QDATE: val = None else: val = qt_to_dt(val) diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py index 529055ecd2..40f7a2e4e0 100644 --- a/src/calibre/gui2/library/delegates.py +++ b/src/calibre/gui2/library/delegates.py @@ -96,7 +96,7 @@ class DateDelegate(QStyledItemDelegate): # {{{ def displayText(self, val, locale): d = val.toDate() - if d == UNDEFINED_QDATE: + if d <= UNDEFINED_QDATE: return '' return format_date(d.toPyDate(), 'dd MMM yyyy') @@ -116,7 +116,7 @@ class PubDateDelegate(QStyledItemDelegate): # {{{ def displayText(self, val, locale): d = val.toDate() - if d == UNDEFINED_QDATE: + if d <= UNDEFINED_QDATE: return '' format = tweaks['gui_pubdate_display_format'] if format is None: @@ -194,7 +194,7 @@ class CcDateDelegate(QStyledItemDelegate): # {{{ def displayText(self, val, locale): d = val.toDate() - if d == UNDEFINED_QDATE: + if d <= UNDEFINED_QDATE: return '' return format_date(d.toPyDate(), self.format) @@ -217,7 +217,7 @@ class CcDateDelegate(QStyledItemDelegate): # {{{ def setModelData(self, editor, model, index): val = editor.date() - if val == UNDEFINED_QDATE: + if val <= UNDEFINED_QDATE: val = None model.setData(index, QVariant(val), Qt.EditRole) diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index d46ae23d90..af950a36fc 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -209,13 +209,13 @@ class ResultCache(SearchQueryParser): if query == 'false': for item in self._data: if item is None: continue - if item[loc] is None or item[loc] == UNDEFINED_DATE: + if item[loc] is None or item[loc] <= UNDEFINED_DATE: matches.add(item[0]) return matches if query == 'true': for item in self._data: if item is None: continue - if item[loc] is not None and item[loc] != UNDEFINED_DATE: + if item[loc] is not None and item[loc] > UNDEFINED_DATE: matches.add(item[0]) return matches diff --git a/src/calibre/utils/date.py b/src/calibre/utils/date.py index c5fba13f57..b25940d23d 100644 --- a/src/calibre/utils/date.py +++ b/src/calibre/utils/date.py @@ -44,7 +44,7 @@ parse_date_day_first = compute_locale_info_for_parse_date() utc_tz = _utc_tz = tzutc() local_tz = _local_tz = SafeLocalTimeZone() -UNDEFINED_DATE = datetime(101,1,1, tzinfo=utc_tz) +UNDEFINED_DATE = datetime(1000,1,1, tzinfo=utc_tz) def parse_date(date_string, assume_utc=False, as_utc=True, default=None): ''' From abdee7d012c253f198271a9ea838e3e447807f6c Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 11 Jul 2010 10:51:56 +0100 Subject: [PATCH 13/38] Disable editing metadata on the device view when automatic update is chosen. --- src/calibre/gui2/library/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 9f1a72b021..89008735fe 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -1216,7 +1216,9 @@ class DeviceBooksModel(BooksModel): # {{{ return done def set_editable(self, editable): - self.editable = editable + # Cannot edit if metadata is sent on connect. Reason: changes will + # revert to what is in the library on next connect. + self.editable = editable and prefs['manage_device_metadata']!='on_connect' def set_search_restriction(self, s): pass From 30436a9e6aa58ff5009733ccbb9f71519d8e4ffb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 11 Jul 2010 09:28:27 -0600 Subject: [PATCH 14/38] Fix #6100 (Improved aldiko support) --- src/calibre/devices/android/driver.py | 10 ++++++++++ src/calibre/devices/usbms/device.py | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 5642235b31..9951dc57ad 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -70,6 +70,16 @@ class ANDROID(USBMS): dirs = [x.strip() for x in dirs.split(',')] self.EBOOK_DIR_MAIN = dirs + def get_main_ebook_dir(self, for_upload=False): + dirs = self.EBOOK_DIR_MAIN + if not for_upload: + def aldiko_tweak(x): + return 'eBooks' if x == 'eBooks/import' else x + if isinstance(dirs, basestring): + dirs = [dirs] + dirs = list(map(aldiko_tweak, dirs)) + return dirs + class S60(USBMS): name = 'S60 driver' diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index 55790420f2..c07b7fd761 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -732,7 +732,7 @@ class Device(DeviceConfig, DevicePlugin): traceback.print_exc() self._main_prefix = self._card_a_prefix = self._card_b_prefix = None - def get_main_ebook_dir(self): + def get_main_ebook_dir(self, for_upload=False): return self.EBOOK_DIR_MAIN def _sanity_check(self, on_card, files): @@ -750,7 +750,7 @@ class Device(DeviceConfig, DevicePlugin): path = os.path.join(self._card_b_prefix, *(self.EBOOK_DIR_CARD_B.split('/'))) else: - candidates = self.get_main_ebook_dir() + candidates = self.get_main_ebook_dir(for_upload=True) if isinstance(candidates, basestring): candidates = [candidates] candidates = [ From ba94ea9670eb7010328d721830881199cee1a842 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 11 Jul 2010 09:47:41 -0600 Subject: [PATCH 15/38] oops --- src/calibre/gui2/convert/structure_detection.ui | 6 +----- src/calibre/gui2/custom_column_widgets.py | 9 ++------- src/calibre/gui2/library/delegates.py | 8 ++++---- src/calibre/gui2/library/models.py | 4 +--- src/calibre/library/caches.py | 4 ++-- src/calibre/utils/date.py | 2 +- 6 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/calibre/gui2/convert/structure_detection.ui b/src/calibre/gui2/convert/structure_detection.ui index 67fd0a31b2..e4414473f5 100644 --- a/src/calibre/gui2/convert/structure_detection.ui +++ b/src/calibre/gui2/convert/structure_detection.ui @@ -28,11 +28,7 @@ - - - 30 - - + diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index 58c2f15452..2a9c81e8ee 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -132,11 +132,9 @@ class DateEdit(QDateEdit): def focusInEvent(self, x): self.setSpecialValueText('') - QDateEdit.focusInEvent(self, x) def focusOutEvent(self, x): self.setSpecialValueText(_('Undefined')) - QDateEdit.focusOutEvent(self, x) class DateTime(Base): @@ -144,10 +142,7 @@ class DateTime(Base): self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), DateEdit(parent)] w = self.widgets[1] - format = self.col_metadata['display'].get('date_format','') - if not format: - format = 'dd MMM yyyy' - w.setDisplayFormat(format) + w.setDisplayFormat('dd MMM yyyy') w.setCalendarPopup(True) w.setMinimumDate(UNDEFINED_QDATE) w.setSpecialValueText(_('Undefined')) @@ -161,7 +156,7 @@ class DateTime(Base): def getter(self): val = self.widgets[1].date() - if val <= UNDEFINED_QDATE: + if val == UNDEFINED_QDATE: val = None else: val = qt_to_dt(val) diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py index 40f7a2e4e0..529055ecd2 100644 --- a/src/calibre/gui2/library/delegates.py +++ b/src/calibre/gui2/library/delegates.py @@ -96,7 +96,7 @@ class DateDelegate(QStyledItemDelegate): # {{{ def displayText(self, val, locale): d = val.toDate() - if d <= UNDEFINED_QDATE: + if d == UNDEFINED_QDATE: return '' return format_date(d.toPyDate(), 'dd MMM yyyy') @@ -116,7 +116,7 @@ class PubDateDelegate(QStyledItemDelegate): # {{{ def displayText(self, val, locale): d = val.toDate() - if d <= UNDEFINED_QDATE: + if d == UNDEFINED_QDATE: return '' format = tweaks['gui_pubdate_display_format'] if format is None: @@ -194,7 +194,7 @@ class CcDateDelegate(QStyledItemDelegate): # {{{ def displayText(self, val, locale): d = val.toDate() - if d <= UNDEFINED_QDATE: + if d == UNDEFINED_QDATE: return '' return format_date(d.toPyDate(), self.format) @@ -217,7 +217,7 @@ class CcDateDelegate(QStyledItemDelegate): # {{{ def setModelData(self, editor, model, index): val = editor.date() - if val <= UNDEFINED_QDATE: + if val == UNDEFINED_QDATE: val = None model.setData(index, QVariant(val), Qt.EditRole) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 89008735fe..9f1a72b021 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -1216,9 +1216,7 @@ class DeviceBooksModel(BooksModel): # {{{ return done def set_editable(self, editable): - # Cannot edit if metadata is sent on connect. Reason: changes will - # revert to what is in the library on next connect. - self.editable = editable and prefs['manage_device_metadata']!='on_connect' + self.editable = editable def set_search_restriction(self, s): pass diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index af950a36fc..d46ae23d90 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -209,13 +209,13 @@ class ResultCache(SearchQueryParser): if query == 'false': for item in self._data: if item is None: continue - if item[loc] is None or item[loc] <= UNDEFINED_DATE: + if item[loc] is None or item[loc] == UNDEFINED_DATE: matches.add(item[0]) return matches if query == 'true': for item in self._data: if item is None: continue - if item[loc] is not None and item[loc] > UNDEFINED_DATE: + if item[loc] is not None and item[loc] != UNDEFINED_DATE: matches.add(item[0]) return matches diff --git a/src/calibre/utils/date.py b/src/calibre/utils/date.py index b25940d23d..c5fba13f57 100644 --- a/src/calibre/utils/date.py +++ b/src/calibre/utils/date.py @@ -44,7 +44,7 @@ parse_date_day_first = compute_locale_info_for_parse_date() utc_tz = _utc_tz = tzutc() local_tz = _local_tz = SafeLocalTimeZone() -UNDEFINED_DATE = datetime(1000,1,1, tzinfo=utc_tz) +UNDEFINED_DATE = datetime(101,1,1, tzinfo=utc_tz) def parse_date(date_string, assume_utc=False, as_utc=True, default=None): ''' From de0aea0eb7630ed467997d66d15f7b6790b10483 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 11 Jul 2010 09:52:15 -0600 Subject: [PATCH 16/38] Possible fix for conversion box being too wide --- src/calibre/gui2/convert/structure_detection.ui | 6 +++++- src/calibre/gui2/convert/xexp_edit.ui | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/convert/structure_detection.ui b/src/calibre/gui2/convert/structure_detection.ui index e4414473f5..2e97c0d3ca 100644 --- a/src/calibre/gui2/convert/structure_detection.ui +++ b/src/calibre/gui2/convert/structure_detection.ui @@ -28,7 +28,11 @@ - + + + 20 + + diff --git a/src/calibre/gui2/convert/xexp_edit.ui b/src/calibre/gui2/convert/xexp_edit.ui index 1b0196a8a1..f98eb8b1b8 100644 --- a/src/calibre/gui2/convert/xexp_edit.ui +++ b/src/calibre/gui2/convert/xexp_edit.ui @@ -43,6 +43,15 @@ 0 + + + 500 + 16777215 + + + + 30 + From 924b8a3b3eac3afdf3059dc2a7df7002049bd8a0 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 11 Jul 2010 17:16:03 +0100 Subject: [PATCH 17/38] Remove change to default date, reverting it to year 101 --- src/calibre/utils/date.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/utils/date.py b/src/calibre/utils/date.py index b25940d23d..c5fba13f57 100644 --- a/src/calibre/utils/date.py +++ b/src/calibre/utils/date.py @@ -44,7 +44,7 @@ parse_date_day_first = compute_locale_info_for_parse_date() utc_tz = _utc_tz = tzutc() local_tz = _local_tz = SafeLocalTimeZone() -UNDEFINED_DATE = datetime(1000,1,1, tzinfo=utc_tz) +UNDEFINED_DATE = datetime(101,1,1, tzinfo=utc_tz) def parse_date(date_string, assume_utc=False, as_utc=True, default=None): ''' From acdc3d864a4402dc6df778d1cbc7d8de2da43233 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 12 Jul 2010 10:05:14 +0100 Subject: [PATCH 18/38] Add 'set to today' buttons to metadata editors --- src/calibre/gui2/custom_column_widgets.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index 58c2f15452..fed1de93a1 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -10,9 +10,10 @@ from functools import partial from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateEdit, \ QDate, QGroupBox, QVBoxLayout, QPlainTextEdit, QSizePolicy, \ - QSpacerItem, QIcon, QCheckBox, QWidget, QHBoxLayout, SIGNAL + QSpacerItem, QIcon, QCheckBox, QWidget, QHBoxLayout, SIGNAL, \ + QPushButton -from calibre.utils.date import qt_to_dt +from calibre.utils.date import qt_to_dt, now from calibre.gui2.widgets import TagsLineEdit, EnComboBox from calibre.gui2 import UNDEFINED_QDATE from calibre.utils.config import tweaks @@ -138,19 +139,24 @@ class DateEdit(QDateEdit): self.setSpecialValueText(_('Undefined')) QDateEdit.focusOutEvent(self, x) + def set_to_today(self): + self.setDate(now()) + class DateTime(Base): def setup_ui(self, parent): - self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), - DateEdit(parent)] + cm = self.col_metadata + self.widgets = [QLabel('&'+cm['name']+':', parent), DateEdit(parent), + QLabel(''), QPushButton(_('Set \'%s\' to today')%cm['name'], parent)] w = self.widgets[1] - format = self.col_metadata['display'].get('date_format','') + format = cm['display'].get('date_format','') if not format: format = 'dd MMM yyyy' w.setDisplayFormat(format) w.setCalendarPopup(True) w.setMinimumDate(UNDEFINED_QDATE) w.setSpecialValueText(_('Undefined')) + self.widgets[3].clicked.connect(w.set_to_today) def setter(self, val): if val is None: From 0e64fff394eef54ae77ce33329fef1a8b7648e7e Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 12 Jul 2010 10:15:43 +0100 Subject: [PATCH 19/38] Put back some of the changes that the merge from trunk removed. --- src/calibre/gui2/custom_column_widgets.py | 2 ++ src/calibre/gui2/library/models.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index 14a1a4abdc..96232fe85f 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -133,9 +133,11 @@ class DateEdit(QDateEdit): def focusInEvent(self, x): self.setSpecialValueText('') + QDateEdit.focusInEvent(self, x) def focusOutEvent(self, x): self.setSpecialValueText(_('Undefined')) + QDateEdit.focusOutEvent(self, x) def set_to_today(self): self.setDate(now()) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 9f1a72b021..89008735fe 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -1216,7 +1216,9 @@ class DeviceBooksModel(BooksModel): # {{{ return done def set_editable(self, editable): - self.editable = editable + # Cannot edit if metadata is sent on connect. Reason: changes will + # revert to what is in the library on next connect. + self.editable = editable and prefs['manage_device_metadata']!='on_connect' def set_search_restriction(self, s): pass From f2dd86faecc7296caa982479f2459de9709147b7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 12 Jul 2010 07:56:13 -0600 Subject: [PATCH 20/38] Fix #6152 ("Edit meta information" should be "Edit metadata") --- src/calibre/gui2/layout.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index c44efa2354..1c853cbdff 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -403,7 +403,7 @@ class MainWindowMixin(object): ac('add', _('Add books'), 'add_book.svg', _('A')) ac('del', _('Remove books'), 'trash.svg', _('Del')) - ac('edit', _('Edit meta info'), 'edit_input.svg', _('E')) + ac('edit', _('Edit metadata'), 'edit_input.svg', _('E')) ac('merge', _('Merge book records'), 'merge_books.svg', _('M')) ac('sync', _('Send to device'), 'sync.svg') ac('save', _('Save to disk'), 'save.svg', _('S')) From e16310c70a3ef7548c5dc9aa8ff37af50090bfa5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 12 Jul 2010 08:08:12 -0600 Subject: [PATCH 21/38] El PAin Impresso by DM --- resources/images/news/elpais_impreso.png | Bin 0 -> 717 bytes resources/recipes/elpais_impreso.recipe | 86 +++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 resources/images/news/elpais_impreso.png create mode 100644 resources/recipes/elpais_impreso.recipe diff --git a/resources/images/news/elpais_impreso.png b/resources/images/news/elpais_impreso.png new file mode 100644 index 0000000000000000000000000000000000000000..35dcaf2d44455f3e97f3985c9df6483463370f5b GIT binary patch literal 717 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87#P2Kx;TbdoL)M4zyIYxk)!tO!&q2VS{L4! z)X=07)$*(RXk&tjmxYDoH;<#Op{wKG z`Zryz`dTXL!1(ysR__0Ec1<~1{Q2GHG|oj#7VGX;o~qC~_v410+*!R(HUENZ)3Pj1 z{+!&-*<{qZ)#k0NA%hZwQdh@@9dnd-@8WZK7v0D|)5bdLw&$D9;{Ok;~o&qZPnZ!E0qTk?yesUhbb`wkAJrVbO22?0#!0(j3_mMR^6(OBp9 zO}%3Cmzkf=^UB%Oxi5`zx$+@SN|i%N?)}m%jVGV7YtGaxj99hc)yG9g%C;MsOjxjV zt#Bp_#~vXMjm5_j-o548{@yn6+gGEs2S<$yYbJE3yZsDZ_h7c_k5}g$byF5Bd$4Fv z#0jC-clx>}O1(%z9xt1G$hX|uCeOL~+s3%q}slw#4@vGG@Jv-WiV&D+;Y zhq~@jSaeoRs&D4%^w*3tXZaYL&itqCk=C^0!PdYgtr?H+e9`S{UB4r$$6PpiI>!dx zTTN!h1~OcHWo2Iyo)w%sAoFa^!t1RMF7O2CJ=^|8nz4>qey*ru;cO3mU~*6`ag8WR zNi0dVN-jzTQVd20h6cI@=DLPPAqEy!24+?!Cb~e Date: Mon, 12 Jul 2010 08:15:20 -0600 Subject: [PATCH 22/38] ... --- resources/recipes/ap.recipe | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/recipes/ap.recipe b/resources/recipes/ap.recipe index 572c0aa392..0118cf0726 100644 --- a/resources/recipes/ap.recipe +++ b/resources/recipes/ap.recipe @@ -12,9 +12,9 @@ class AssociatedPress(BasicNewsRecipe): max_articles_per_feed = 15 html2lrf_options = ['--force-page-break-before-tag="chapter"'] - - - preprocess_regexps = [ (re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in + + + preprocess_regexps = [ (re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in [ (r'.*?' , lambda match : ''), (r'.*?', lambda match : ''), @@ -25,10 +25,10 @@ class AssociatedPress(BasicNewsRecipe): (r'

    ', lambda match : '

    '), (r'Learn more about our Privacy Policy.*?', lambda match : ''), ] - ] - + ] + + - feeds = [ ('AP Headlines', 'http://hosted.ap.org/lineups/TOPHEADS-rss_2.0.xml?SITE=ORAST&SECTION=HOME'), ('AP US News', 'http://hosted.ap.org/lineups/USHEADS-rss_2.0.xml?SITE=CAVIC&SECTION=HOME'), ('AP World News', 'http://hosted.ap.org/lineups/WORLDHEADS-rss_2.0.xml?SITE=SCAND&SECTION=HOME'), @@ -38,4 +38,4 @@ class AssociatedPress(BasicNewsRecipe): ('AP Health News', 'http://hosted.ap.org/lineups/HEALTHHEADS-rss_2.0.xml?SITE=FLDAY&SECTION=HOME'), ('AP Science News', 'http://hosted.ap.org/lineups/SCIENCEHEADS-rss_2.0.xml?SITE=OHCIN&SECTION=HOME'), ('AP Strange News', 'http://hosted.ap.org/lineups/STRANGEHEADS-rss_2.0.xml?SITE=WCNC&SECTION=HOME'), - ] \ No newline at end of file + ] From 6a4ff480599ef7aae33abea1aa9e7facffbd2c6e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 12 Jul 2010 16:55:01 -0600 Subject: [PATCH 23/38] Fix #6156 (Error running Calibre on Xen virtual machine with Ubuntu Lucid 10.04) --- src/calibre/devices/scanner.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/scanner.py b/src/calibre/devices/scanner.py index ceba5d37d0..dd789dd668 100644 --- a/src/calibre/devices/scanner.py +++ b/src/calibre/devices/scanner.py @@ -98,6 +98,9 @@ class LinuxScanner(object): def __call__(self): ans = set([]) + if not self.ok: + raise RuntimeError('DeviceScanner requires the /sys filesystem to work.') + for x in os.listdir(self.base): base = os.path.join(self.base, x) ven = os.path.join(base, 'idVendor') @@ -145,8 +148,6 @@ class DeviceScanner(object): def __init__(self, *args): if isosx and osx_scanner is None: raise RuntimeError('The Python extension usbobserver must be available on OS X.') - if islinux and not linux_scanner.ok: - raise RuntimeError('DeviceScanner requires the /sys filesystem to work.') self.scanner = win_scanner if iswindows else osx_scanner if isosx else linux_scanner self.devices = [] From c56a4a5aaf6b6f3cd360808381aabfb7c0b6e574 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 13 Jul 2010 12:12:03 -0600 Subject: [PATCH 24/38] Fix #6165 (Error communicating with the device) --- resources/images/donate.svg | 53 ++--- resources/images/lt.png | Bin 0 -> 30594 bytes src/calibre/devices/prs505/sony_cache.py | 25 ++- src/calibre/gui2/device.py | 1 + src/calibre/gui2/dialogs/config/__init__.py | 7 - src/calibre/gui2/dialogs/config/config.ui | 48 ----- src/calibre/gui2/init.py | 6 - src/calibre/gui2/layout.py | 214 +++++++++++-------- src/calibre/gui2/search_restriction_mixin.py | 1 + src/calibre/gui2/ui.py | 9 +- 10 files changed, 169 insertions(+), 195 deletions(-) create mode 100644 resources/images/lt.png diff --git a/resources/images/donate.svg b/resources/images/donate.svg index b17d0ec7a0..603e672f6f 100644 --- a/resources/images/donate.svg +++ b/resources/images/donate.svg @@ -1,24 +1,31 @@ + + sodipodi:docname="donate.svg"> + @@ -180,8 +187,8 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="7.851329" - inkscape:cx="92.691163" - inkscape:cy="92.473338" + inkscape:cx="60.937831" + inkscape:cy="61.488995" inkscape:current-layer="layer1" showgrid="false" inkscape:document-units="px" @@ -189,10 +196,11 @@ guidetolerance="0.1px" showguides="true" inkscape:guide-bbox="true" - inkscape:window-width="1106" - inkscape:window-height="958" - inkscape:window-x="597" - inkscape:window-y="25"> + inkscape:window-width="1680" + inkscape:window-height="997" + inkscape:window-x="-4" + inkscape:window-y="30" + inkscape:window-maximized="1"> - - + + + Oz@Z0f2-7z;ux~O9+4z06=<WDR*FRcSTFz- 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 literal 0 HcmV?d00001 diff --git a/src/calibre/devices/prs505/sony_cache.py b/src/calibre/devices/prs505/sony_cache.py index b87ca937bc..3ac35df9b2 100644 --- a/src/calibre/devices/prs505/sony_cache.py +++ b/src/calibre/devices/prs505/sony_cache.py @@ -10,10 +10,10 @@ from base64 import b64decode from uuid import uuid4 from lxml import etree -from calibre import prints, guess_type +from calibre import prints, guess_type, isbytestring from calibre.devices.errors import DeviceError from calibre.devices.usbms.driver import debug_print -from calibre.constants import DEBUG +from calibre.constants import DEBUG, preferred_encoding from calibre.ebooks.chardet import xml_to_unicode from calibre.ebooks.metadata import authors_to_string, title_sort @@ -473,6 +473,13 @@ class XMLCache(object): # if the case of a tie, and hope it is right. timestamp = os.path.getmtime(path) rec_date = record.get('date', None) + + def clean(x): + if isbytestring(x): + x = x.decode(preferred_encoding, 'replace') + x.replace(u'\0', '') + return x + if not getattr(book, '_new_book', False): # book is not new if strftime(timestamp, zone=time.gmtime) == rec_date: gtz_count += 1 @@ -486,19 +493,19 @@ class XMLCache(object): tz = time.gmtime debug_print("Using GMT TZ for new book", book.lpath) date = strftime(timestamp, zone=tz) - record.set('date', date) + record.set('date', clean(date)) - record.set('size', str(os.stat(path).st_size)) + record.set('size', clean(str(os.stat(path).st_size))) title = book.title if book.title else _('Unknown') - record.set('title', title) + record.set('title', clean(title)) ts = book.title_sort if not ts: ts = title_sort(title) - record.set('titleSorter', ts) + record.set('titleSorter', clean(ts)) if self.use_author_sort and book.author_sort is not None: - record.set('author', book.author_sort) + record.set('author', clean(book.author_sort)) else: - record.set('author', authors_to_string(book.authors)) + record.set('author', clean(authors_to_string(book.authors))) ext = os.path.splitext(path)[1] if ext: ext = ext[1:].lower() @@ -506,7 +513,7 @@ class XMLCache(object): if mime is None: mime = guess_type('a.'+ext)[0] if mime is not None: - record.set('mime', mime) + record.set('mime', clean(mime)) if 'sourceid' not in record.attrib: record.set('sourceid', '1') if 'id' not in record.attrib: diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 91afac8aa2..d81918c307 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -765,6 +765,7 @@ class DeviceMixin(object): # {{{ self.book_details.reset_info() self.location_view.setCurrentIndex(self.location_view.model().index(0)) self.refresh_ondevice_info (device_connected = False) + self.tool_bar.device_status_changed(bool(connected)) def info_read(self, job): ''' diff --git a/src/calibre/gui2/dialogs/config/__init__.py b/src/calibre/gui2/dialogs/config/__init__.py index bf9dc0a623..b064dc53c2 100644 --- a/src/calibre/gui2/dialogs/config/__init__.py +++ b/src/calibre/gui2/dialogs/config/__init__.py @@ -334,7 +334,6 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): def __init__(self, parent, library_view, server=None): ResizableDialog.__init__(self, parent) - self.ICON_SIZES = {0:QSize(48, 48), 1:QSize(32,32), 2:QSize(24,24)} self._category_model = CategoryModel() self.category_view.currentChanged = self.category_current_changed @@ -389,10 +388,6 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): self.add_custcol_button.clicked.connect(self.add_custcol) self.edit_custcol_button.clicked.connect(self.edit_custcol) - icons = config['toolbar_icon_size'] - self.toolbar_button_size.setCurrentIndex(0 if icons == self.ICON_SIZES[0] else 1 if icons == self.ICON_SIZES[1] else 2) - self.show_toolbar_text.setChecked(config['show_text_in_toolbar']) - output_formats = sorted(available_output_formats()) output_formats.remove('oeb') for f in output_formats: @@ -845,8 +840,6 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): must_restart = self.apply_custom_column_changes() - config['toolbar_icon_size'] = self.ICON_SIZES[self.toolbar_button_size.currentIndex()] - config['show_text_in_toolbar'] = bool(self.show_toolbar_text.isChecked()) config['separate_cover_flow'] = bool(self.separate_cover_flow.isChecked()) config['disable_tray_notification'] = not self.systray_notifications.isChecked() p = {0:'normal', 1:'high', 2:'low'}[self.priority.currentIndex()] diff --git a/src/calibre/gui2/dialogs/config/config.ui b/src/calibre/gui2/dialogs/config/config.ui index b473ee7846..5f890631b2 100644 --- a/src/calibre/gui2/dialogs/config/config.ui +++ b/src/calibre/gui2/dialogs/config/config.ui @@ -422,54 +422,6 @@ - - - Toolbar - - - - - - - Large - - - - - Medium - - - - - Small - - - - - - - - &Button size in toolbar - - - toolbar_button_size - - - - - - - Show &text in toolbar buttons - - - true - - - - - - - diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 2d038d9ddc..2474685522 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -176,12 +176,6 @@ class ToolbarMixin(object): # {{{ def show_help(self, *args): open_url(QUrl('http://calibre-ebook.com/user_manual')) - def read_toolbar_settings(self): - self.tool_bar.setIconSize(config['toolbar_icon_size']) - self.tool_bar.setToolButtonStyle( - Qt.ToolButtonTextUnderIcon if \ - config['show_text_in_toolbar'] else \ - Qt.ToolButtonIconOnly) # }}} diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index 1c853cbdff..0228249e8d 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -5,6 +5,8 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' +from operator import attrgetter + from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, QVariant, \ QAbstractListModel, QFont, QApplication, QPalette, pyqtSignal, QToolButton, \ QModelIndex, QListView, QAbstractButton, QPainter, QPixmap, QColor, \ @@ -13,41 +15,11 @@ from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, QVariant, \ from calibre.constants import __appname__, filesystem_encoding from calibre.gui2.search_box import SearchBox2, SavedSearchBox from calibre.gui2.throbber import ThrobbingButton -from calibre.gui2 import NONE +from calibre.gui2 import NONE, config from calibre.gui2.widgets import ComboBoxWithHelp from calibre import human_readable -class ToolBar(QToolBar): # {{{ - - def __init__(self, parent=None): - QToolBar.__init__(self, parent) - self.setContextMenuPolicy(Qt.PreventContextMenu) - self.setMovable(False) - self.setFloatable(False) - self.setOrientation(Qt.Horizontal) - self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea) - self.setIconSize(QSize(48, 48)) - self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) - - def add_actions(self, *args): - self.left_space = QWidget(self) - self.left_space.setSizePolicy(QSizePolicy.Expanding, - QSizePolicy.Minimum) - self.addWidget(self.left_space) - for action in args: - if action is None: - self.addSeparator() - else: - self.addAction(action) - self.right_space = QWidget(self) - self.right_space.setSizePolicy(QSizePolicy.Expanding, - QSizePolicy.Minimum) - self.addWidget(self.right_space) - - def contextMenuEvent(self, *args): - pass - -# }}} +ICON_SIZE = 48 # Location View {{{ @@ -191,14 +163,15 @@ class LocationView(QListView): self.setTabKeyNavigation(True) self.setProperty("showDropIndicator", True) self.setSelectionMode(self.SingleSelection) - self.setIconSize(QSize(40, 40)) + self.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) self.setMovement(self.Static) self.setFlow(self.LeftToRight) - self.setGridSize(QSize(175, 90)) + self.setGridSize(QSize(175, ICON_SIZE)) self.setViewMode(self.ListMode) self.setWordWrap(True) self.setObjectName("location_view") - self.setMaximumHeight(74) + self.setMaximumSize(QSize(600, ICON_SIZE+16)) + self.setMinimumWidth(400) def eject_clicked(self, *args): self.unmount_device.emit() @@ -207,6 +180,10 @@ class LocationView(QListView): self.model().count = new_count self.model().reset() + @property + def book_count(self): + return self.model().count + def current_changed(self, current, previous): if current.isValid(): i = current.row() @@ -248,12 +225,15 @@ class EjectButton(QAbstractButton): def __init__(self, parent): QAbstractButton.__init__(self, parent) self.mouse_over = False + self.setMouseTracking(True) def enterEvent(self, event): self.mouse_over = True + QAbstractButton.enterEvent(self, event) def leaveEvent(self, event): self.mouse_over = False + QAbstractButton.leaveEvent(self, event) def paintEvent(self, event): painter = QPainter(self) @@ -344,33 +324,84 @@ class SearchBar(QWidget): # {{{ # }}} -class LocationBar(ToolBar): # {{{ +class ToolBar(QToolBar): # {{{ def __init__(self, actions, donate, location_view, parent=None): - ToolBar.__init__(self, parent) - - for ac in actions: - self.addAction(ac) - - self.addWidget(location_view) - self.w = QWidget() - self.w.setLayout(QVBoxLayout()) - self.w.layout().addWidget(donate) - donate.setAutoRaise(True) - donate.setCursor(Qt.PointingHandCursor) - self.addWidget(self.w) - self.setIconSize(QSize(50, 50)) + QToolBar.__init__(self, parent) + self.setContextMenuPolicy(Qt.PreventContextMenu) + self.setMovable(False) + self.setFloatable(False) + self.setOrientation(Qt.Horizontal) + self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea) + self.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) - def button_for_action(self, ac): - b = QToolButton(self) - b.setDefaultAction(ac) - for x in ('ToolTip', 'StatusTip', 'WhatsThis'): - getattr(b, 'set'+x)(b.text()) + self.showing_device = False + self.all_actions = actions + self.donate = donate + self.location_view = location_view + self.d_widget = QWidget() + self.d_widget.setLayout(QVBoxLayout()) + self.d_widget.layout().addWidget(donate) + donate.setAutoRaise(True) + donate.setCursor(Qt.PointingHandCursor) + self.build_bar() + + def contextMenuEvent(self, *args): + pass + + def device_status_changed(self, connected): + self.showing_device = connected + self.build_bar() + + def build_bar(self): + order_field = 'device' if self.showing_device else 'normal' + o = attrgetter(order_field+'_order') + sepvals = [2] if self.showing_device else [1] + sepvals += [3] + actions = [x for x in self.all_actions if o(x) > -1] + actions.sort(cmp=lambda x,y : cmp(o(x), o(y))) + self.clear() + for x in actions: + self.addAction(x) + ch = self.widgetForAction(x) + ch.setCursor(Qt.PointingHandCursor) + ch.setAutoRaise(True) + + if x.action_name == 'choose_library': + self.location_action = self.addWidget(self.location_view) + self.choose_action = x + if config['show_donate_button']: + self.addWidget(self.d_widget) + if x.action_name not in ('choose_library', 'help'): + ch.setPopupMode(ch.MenuButtonPopup) + + + for x in actions: + if x.separator_before in sepvals: + self.insertSeparator(x) + + + self.location_action.setVisible(self.showing_device) + self.choose_action.setVisible(not self.showing_device) + + def count_changed(self, new_count): + text = _('%d books')%new_count + a = self.choose_action + a.setText(text) + + def resizeEvent(self, ev): + style = Qt.ToolButtonTextUnderIcon + if self.size().width() < 1260: + style = Qt.ToolButtonIconOnly + self.setToolButtonStyle(style) + QToolBar.resizeEvent(self, ev) - return b # }}} +class Action(QAction): + pass + class MainWindowMixin(object): def __init__(self): @@ -385,12 +416,19 @@ class MainWindowMixin(object): self.centralwidget.setLayout(self._central_widget_layout) self.resize(1012, 740) self.donate_button = ThrobbingButton(self.centralwidget) - self.donate_button.set_normal_icon_size(64, 64) + self.donate_button.set_normal_icon_size(ICON_SIZE, ICON_SIZE) # Actions {{{ - def ac(name, text, icon, shortcut=None, tooltip=None): - action = QAction(QIcon(I(icon)), text, self) + all_actions = [] + + def ac(normal_order, device_order, separator_before, + name, text, icon, shortcut=None, tooltip=None): + action = Action(QIcon(I(icon)), text, self) + action.normal_order = normal_order + action.device_order = device_order + action.separator_before = separator_before + action.action_name = name text = tooltip if tooltip else text action.setToolTip(text) action.setStatusTip(text) @@ -400,56 +438,46 @@ class MainWindowMixin(object): if shortcut: action.setShortcut(shortcut) setattr(self, 'action_'+name, action) + all_actions.append(action) - ac('add', _('Add books'), 'add_book.svg', _('A')) - ac('del', _('Remove books'), 'trash.svg', _('Del')) - ac('edit', _('Edit metadata'), 'edit_input.svg', _('E')) - ac('merge', _('Merge book records'), 'merge_books.svg', _('M')) - ac('sync', _('Send to device'), 'sync.svg') - ac('save', _('Save to disk'), 'save.svg', _('S')) - ac('news', _('Fetch news'), 'news.svg', _('F')) - ac('convert', _('Convert books'), 'convert.svg', _('C')) - ac('view', _('View'), 'view.svg', _('V')) - ac('open_containing_folder', _('Open containing folder'), + ac(0, 7, 0, 'add', _('Add books'), 'add_book.svg', _('A')) + ac(1, 1, 0, 'edit', _('Edit metadata'), 'edit_input.svg', _('E')) + ac(2, 2, 3, 'convert', _('Convert books'), 'convert.svg', _('C')) + ac(3, 3, 0, 'view', _('View'), 'view.svg', _('V')) + ac(4, 4, 3, 'choose_library', _('%d books')%0, 'lt.png', + tooltip=_('Choose calibre library to work with')) + ac(5, 5, 3, 'news', _('Fetch news'), 'news.svg', _('F')) + ac(6, 6, 0, 'save', _('Save to disk'), 'save.svg', _('S')) + ac(7, 0, 0, 'sync', _('Send to device'), 'sync.svg') + ac(8, 8, 3, 'del', _('Remove books'), 'trash.svg', _('Del')) + ac(9, 9, 3, 'help', _('Help'), 'help.svg', _('F1'), _("Browse the calibre User Manual")) + ac(10, 10, 0, 'preferences', _('Preferences'), 'config.svg', _('Ctrl+P')) + + ac(-1, -1, 0, 'merge', _('Merge book records'), 'merge_books.svg', _('M')) + ac(-1, -1, 0, 'open_containing_folder', _('Open containing folder'), 'document_open.svg') - ac('show_book_details', _('Show book details'), + ac(-1, -1, 0, 'show_book_details', _('Show book details'), 'dialog_information.svg') - ac('books_by_same_author', _('Books by same author'), + ac(-1, -1, 0, 'books_by_same_author', _('Books by same author'), 'user_profile.svg') - ac('books_in_this_series', _('Books in this series'), + ac(-1, -1, 0, 'books_in_this_series', _('Books in this series'), 'books_in_series.svg') - ac('books_by_this_publisher', _('Books by this publisher'), + ac(-1, -1, 0, 'books_by_this_publisher', _('Books by this publisher'), 'publisher.png') - ac('books_with_the_same_tags', _('Books with the same tags'), + ac(-1, -1, 0, 'books_with_the_same_tags', _('Books with the same tags'), 'tags.svg') - ac('preferences', _('Preferences'), 'config.svg', _('Ctrl+P')) - ac('help', _('Help'), 'help.svg', _('F1'), _("Browse the calibre User Manual")) # }}} - self.tool_bar = ToolBar(self) - self.addToolBar(Qt.BottomToolBarArea, self.tool_bar) - self.tool_bar.add_actions(self.action_convert, self.action_view, - None, self.action_edit, None, - self.action_save, self.action_del, - None, - self.action_help, None, self.action_preferences) - self.location_view = LocationView(self.centralwidget) self.search_bar = SearchBar(self) - self.location_bar = LocationBar([self.action_add, self.action_sync, - self.action_news], self.donate_button, self.location_view, self) - self.addToolBar(Qt.TopToolBarArea, self.location_bar) + self.tool_bar = ToolBar(all_actions, self.donate_button, self.location_view, self) + self.addToolBar(Qt.TopToolBarArea, self.tool_bar) l = self.centralwidget.layout() l.addWidget(self.search_bar) - for ch in list(self.tool_bar.children()) + list(self.location_bar.children()): - if isinstance(ch, QToolButton): - ch.setCursor(Qt.PointingHandCursor) - ch.setAutoRaise(True) - if ch is not self.donate_button: - ch.setPopupMode(ch.MenuButtonPopup) - + def read_toolbar_settings(self): + pass diff --git a/src/calibre/gui2/search_restriction_mixin.py b/src/calibre/gui2/search_restriction_mixin.py index f677c839d8..a4186ad8d1 100644 --- a/src/calibre/gui2/search_restriction_mixin.py +++ b/src/calibre/gui2/search_restriction_mixin.py @@ -13,6 +13,7 @@ class SearchRestrictionMixin(object): self.search_restriction.setSizeAdjustPolicy(self.search_restriction.AdjustToMinimumContentsLengthWithIcon) self.search_restriction.setMinimumContentsLength(10) self.search_restriction.setStatusTip(self.search_restriction.toolTip()) + self.search_count.setText(_("(all books)")) ''' Adding and deleting books while restricted creates a complexity. When added, diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 6bd7b2b502..ba4c637932 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -167,8 +167,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{ self.eject_action = self.system_tray_menu.addAction( QIcon(I('eject.svg')), _('&Eject connected device')) self.eject_action.setEnabled(False) - if not config['show_donate_button']: - self.donate_button.setVisible(False) self.addAction(self.quit_action) self.action_restart = QAction(_('&Restart'), self) self.addAction(self.action_restart) @@ -220,8 +218,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{ if self.system_tray_icon.isVisible() and opts.start_in_tray: self.hide_windows() - self.library_view.model().count_changed_signal.connect \ - (self.location_view.count_changed) + for t in (self.location_view, self.tool_bar): + self.library_view.model().count_changed_signal.connect \ + (t.count_changed) if not gprefs.get('quick_start_guide_added', False): from calibre.ebooks.metadata import MetaInformation mi = MetaInformation(_('Calibre Quick Start Guide'), ['John Schember']) @@ -274,8 +273,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{ SIGNAL('start_recipe_fetch(PyQt_PyObject)'), self.download_scheduled_recipe, Qt.QueuedConnection) - self.location_view.setCurrentIndex(self.location_view.model().index(0)) - self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection) AddAction.__init__(self) From 09490d2b0a8bade9374bd09b8d4c124e4fa2a069 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Wed, 14 Jul 2010 15:49:47 +0100 Subject: [PATCH 25/38] Add another variant of the Samsung Galaxy android phone --- src/calibre/devices/android/driver.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 9951dc57ad..5d9d094b26 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -30,7 +30,8 @@ class ANDROID(USBMS): 0x18d1 : { 0x4e11 : [0x0100, 0x226], 0x4e12: [0x0100, 0x226]}, # Samsung - 0x04e8 : { 0x681d : [0x0222, 0x0400], 0x681c : [0x0222, 0x0224]}, + 0x04e8 : { 0x681d : [0x0222, 0x0400], + 0x681c : [0x0222, 0x0224, 0x0400]}, # Acer 0x502 : { 0x3203 : [0x0100]}, From 9a4b661ac67c14c1b1aa6e576ea804f68cccc114 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 14 Jul 2010 08:57:56 -0600 Subject: [PATCH 26/38] Fix google reader recipes --- resources/recipes/greader.recipe | 72 +++++++++++++------------- resources/recipes/greader_uber.recipe | 73 +++++++++++++-------------- 2 files changed, 70 insertions(+), 75 deletions(-) diff --git a/resources/recipes/greader.recipe b/resources/recipes/greader.recipe index cbf4c0226b..75c273b162 100644 --- a/resources/recipes/greader.recipe +++ b/resources/recipes/greader.recipe @@ -1,37 +1,35 @@ -import urllib, re, mechanize -from calibre.web.feeds.recipes import BasicNewsRecipe -from calibre import __appname__ - -class GoogleReader(BasicNewsRecipe): - title = 'Google Reader' - description = 'This recipe downloads feeds you have tagged from your Google Reader account.' - needs_subscription = True - __author__ = 'davec' - base_url = 'http://www.google.com/reader/atom/' - max_articles_per_feed = 50 - get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed - use_embedded_content = True - - def get_browser(self): - br = BasicNewsRecipe.get_browser() - - if self.username is not None and self.password is not None: - request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), - ('service', 'reader'), ('source', __appname__)]) - response = br.open('https://www.google.com/accounts/ClientLogin', request) - sid = re.search('SID=(\S*)', response.read()).group(1) - - cookies = mechanize.CookieJar() - br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) - cookies.set_cookie(mechanize.Cookie(None, 'SID', sid, None, False, '.google.com', True, True, '/', True, False, None, True, '', '', None)) - return br - - - def get_feeds(self): - feeds = [] - soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') - for id in soup.findAll(True, attrs={'name':['id']}): - url = id.contents[0] - feeds.append((re.search('/([^/]*)$', url).group(1), - self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) - return feeds +import urllib, re, mechanize +from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre import __appname__ + +class GoogleReader(BasicNewsRecipe): + title = 'Google Reader' + description = 'This recipe fetches from your Google Reader account unread Starred items and unread Feeds you have placed in a folder via the manage subscriptions feature.' + needs_subscription = True + __author__ = 'davec, rollercoaster, Starson17' + base_url = 'http://www.google.com/reader/atom/' + oldest_article = 365 + max_articles_per_feed = 250 + get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed + use_embedded_content = True + + def get_browser(self): + br = BasicNewsRecipe.get_browser(self) + if self.username is not None and self.password is not None: + request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), + ('service', 'reader'), ('accountType', 'HOSTED_OR_GOOGLE'), ('source', __appname__)]) + response = br.open('https://www.google.com/accounts/ClientLogin', request) + auth = re.search('Auth=(\S*)', response.read()).group(1) + cookies = mechanize.CookieJar() + br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) + br.addheaders = [('Authorization', 'GoogleLogin auth='+auth)] + return br + + def get_feeds(self): + feeds = [] + soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') + for id in soup.findAll(True, attrs={'name':['id']}): + url = id.contents[0] + feeds.append((re.search('/([^/]*)$', url).group(1), + self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) + return feeds diff --git a/resources/recipes/greader_uber.recipe b/resources/recipes/greader_uber.recipe index ee48e7069d..c98762fe28 100644 --- a/resources/recipes/greader_uber.recipe +++ b/resources/recipes/greader_uber.recipe @@ -1,38 +1,35 @@ -import urllib, re, mechanize -from calibre.web.feeds.recipes import BasicNewsRecipe -from calibre import __appname__ - -class GoogleReaderUber(BasicNewsRecipe): - title = 'Google Reader Uber' - description = 'This recipe downloads all unread feedsfrom your Google Reader account.' - needs_subscription = True - __author__ = 'rollercoaster, davec' - base_url = 'http://www.google.com/reader/atom/' - oldest_article = 365 - max_articles_per_feed = 250 - get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed - use_embedded_content = True - - def get_browser(self): - br = BasicNewsRecipe.get_browser() - - if self.username is not None and self.password is not None: - request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), - ('service', 'reader'), ('source', __appname__)]) - response = br.open('https://www.google.com/accounts/ClientLogin', request) - sid = re.search('SID=(\S*)', response.read()).group(1) - - cookies = mechanize.CookieJar() - br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) - cookies.set_cookie(mechanize.Cookie(None, 'SID', sid, None, False, '.google.com', True, True, '/', True, False, None, True, '', '', None)) - return br - - - def get_feeds(self): - feeds = [] - soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') - for id in soup.findAll(True, attrs={'name':['id']}): - url = id.contents[0].replace('broadcast','reading-list') - feeds.append((re.search('/([^/]*)$', url).group(1), - self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) - return feeds +import urllib, re, mechanize +from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre import __appname__ + +class GoogleReaderUber(BasicNewsRecipe): + title = 'Google Reader uber' + description = 'Fetches all feeds from your Google Reader account including the uncategorized items.' + needs_subscription = True + __author__ = 'davec, rollercoaster, Starson17' + base_url = 'http://www.google.com/reader/atom/' + oldest_article = 365 + max_articles_per_feed = 250 + get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed + use_embedded_content = True + + def get_browser(self): + br = BasicNewsRecipe.get_browser(self) + if self.username is not None and self.password is not None: + request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), + ('service', 'reader'), ('accountType', 'HOSTED_OR_GOOGLE'), ('source', __appname__)]) + response = br.open('https://www.google.com/accounts/ClientLogin', request) + auth = re.search('Auth=(\S*)', response.read()).group(1) + cookies = mechanize.CookieJar() + br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) + br.addheaders = [('Authorization', 'GoogleLogin auth='+auth)] + return br + + def get_feeds(self): + feeds = [] + soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') + for id in soup.findAll(True, attrs={'name':['id']}): + url = id.contents[0].replace('broadcast','reading-list') + feeds.append((re.search('/([^/]*)$', url).group(1), + self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) + return feeds From 8a5a5e2ad13d1285afb6f9ed6a6d58787ddbb7e0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 14 Jul 2010 09:01:01 -0600 Subject: [PATCH 27/38] Waco Tribune Herald by rty --- resources/recipes/greader.recipe | 70 +++++++++++++-------------- resources/recipes/greader_uber.recipe | 70 +++++++++++++-------------- resources/recipes/waco_tribune.recipe | 34 +++++++++++++ 3 files changed, 104 insertions(+), 70 deletions(-) create mode 100644 resources/recipes/waco_tribune.recipe diff --git a/resources/recipes/greader.recipe b/resources/recipes/greader.recipe index 75c273b162..2c9d5aa015 100644 --- a/resources/recipes/greader.recipe +++ b/resources/recipes/greader.recipe @@ -1,35 +1,35 @@ -import urllib, re, mechanize -from calibre.web.feeds.recipes import BasicNewsRecipe -from calibre import __appname__ - -class GoogleReader(BasicNewsRecipe): - title = 'Google Reader' - description = 'This recipe fetches from your Google Reader account unread Starred items and unread Feeds you have placed in a folder via the manage subscriptions feature.' - needs_subscription = True - __author__ = 'davec, rollercoaster, Starson17' - base_url = 'http://www.google.com/reader/atom/' - oldest_article = 365 - max_articles_per_feed = 250 - get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed - use_embedded_content = True - - def get_browser(self): - br = BasicNewsRecipe.get_browser(self) - if self.username is not None and self.password is not None: - request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), - ('service', 'reader'), ('accountType', 'HOSTED_OR_GOOGLE'), ('source', __appname__)]) - response = br.open('https://www.google.com/accounts/ClientLogin', request) - auth = re.search('Auth=(\S*)', response.read()).group(1) - cookies = mechanize.CookieJar() - br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) - br.addheaders = [('Authorization', 'GoogleLogin auth='+auth)] - return br - - def get_feeds(self): - feeds = [] - soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') - for id in soup.findAll(True, attrs={'name':['id']}): - url = id.contents[0] - feeds.append((re.search('/([^/]*)$', url).group(1), - self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) - return feeds +import urllib, re, mechanize +from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre import __appname__ + +class GoogleReader(BasicNewsRecipe): + title = 'Google Reader' + description = 'This recipe fetches from your Google Reader account unread Starred items and unread Feeds you have placed in a folder via the manage subscriptions feature.' + needs_subscription = True + __author__ = 'davec, rollercoaster, Starson17' + base_url = 'http://www.google.com/reader/atom/' + oldest_article = 365 + max_articles_per_feed = 250 + get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed + use_embedded_content = True + + def get_browser(self): + br = BasicNewsRecipe.get_browser(self) + if self.username is not None and self.password is not None: + request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), + ('service', 'reader'), ('accountType', 'HOSTED_OR_GOOGLE'), ('source', __appname__)]) + response = br.open('https://www.google.com/accounts/ClientLogin', request) + auth = re.search('Auth=(\S*)', response.read()).group(1) + cookies = mechanize.CookieJar() + br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) + br.addheaders = [('Authorization', 'GoogleLogin auth='+auth)] + return br + + def get_feeds(self): + feeds = [] + soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') + for id in soup.findAll(True, attrs={'name':['id']}): + url = id.contents[0] + feeds.append((re.search('/([^/]*)$', url).group(1), + self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) + return feeds diff --git a/resources/recipes/greader_uber.recipe b/resources/recipes/greader_uber.recipe index c98762fe28..5e02cdef5d 100644 --- a/resources/recipes/greader_uber.recipe +++ b/resources/recipes/greader_uber.recipe @@ -1,35 +1,35 @@ -import urllib, re, mechanize -from calibre.web.feeds.recipes import BasicNewsRecipe -from calibre import __appname__ - -class GoogleReaderUber(BasicNewsRecipe): - title = 'Google Reader uber' - description = 'Fetches all feeds from your Google Reader account including the uncategorized items.' - needs_subscription = True - __author__ = 'davec, rollercoaster, Starson17' - base_url = 'http://www.google.com/reader/atom/' - oldest_article = 365 - max_articles_per_feed = 250 - get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed - use_embedded_content = True - - def get_browser(self): - br = BasicNewsRecipe.get_browser(self) - if self.username is not None and self.password is not None: - request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), - ('service', 'reader'), ('accountType', 'HOSTED_OR_GOOGLE'), ('source', __appname__)]) - response = br.open('https://www.google.com/accounts/ClientLogin', request) - auth = re.search('Auth=(\S*)', response.read()).group(1) - cookies = mechanize.CookieJar() - br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) - br.addheaders = [('Authorization', 'GoogleLogin auth='+auth)] - return br - - def get_feeds(self): - feeds = [] - soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') - for id in soup.findAll(True, attrs={'name':['id']}): - url = id.contents[0].replace('broadcast','reading-list') - feeds.append((re.search('/([^/]*)$', url).group(1), - self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) - return feeds +import urllib, re, mechanize +from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre import __appname__ + +class GoogleReaderUber(BasicNewsRecipe): + title = 'Google Reader uber' + description = 'Fetches all feeds from your Google Reader account including the uncategorized items.' + needs_subscription = True + __author__ = 'davec, rollercoaster, Starson17' + base_url = 'http://www.google.com/reader/atom/' + oldest_article = 365 + max_articles_per_feed = 250 + get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed + use_embedded_content = True + + def get_browser(self): + br = BasicNewsRecipe.get_browser(self) + if self.username is not None and self.password is not None: + request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), + ('service', 'reader'), ('accountType', 'HOSTED_OR_GOOGLE'), ('source', __appname__)]) + response = br.open('https://www.google.com/accounts/ClientLogin', request) + auth = re.search('Auth=(\S*)', response.read()).group(1) + cookies = mechanize.CookieJar() + br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) + br.addheaders = [('Authorization', 'GoogleLogin auth='+auth)] + return br + + def get_feeds(self): + feeds = [] + soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') + for id in soup.findAll(True, attrs={'name':['id']}): + url = id.contents[0].replace('broadcast','reading-list') + feeds.append((re.search('/([^/]*)$', url).group(1), + self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) + return feeds diff --git a/resources/recipes/waco_tribune.recipe b/resources/recipes/waco_tribune.recipe new file mode 100644 index 0000000000..18eb61fb26 --- /dev/null +++ b/resources/recipes/waco_tribune.recipe @@ -0,0 +1,34 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class AdvancedUserRecipe1278773519(BasicNewsRecipe): + title = u'Waco Tribune Herald' + __author__ = 'rty' + pubisher = 'A Robinson Media Company' + description = 'Waco, Texas, Newspaper' + category = 'News, Texas, Waco' + oldest_article = 7 + max_articles_per_feed = 100 + + feeds = [ + (u'News', u'http://www.wacotrib.com/news/index.rss2'), + (u'Sports', u'http://www.wacotrib.com/sports/index.rss2'), + (u'AccessWaco', u'http://www.wacotrib.com/accesswaco/index.rss2'), + (u'Opinions', u'http://www.wacotrib.com/opinion/index.rss2') + ] + + remove_javascript = True + use_embedded_content = False + no_stylesheets = True + language = 'en' + encoding = 'utf-8' + conversion_options = {'linearize_tables':True} + masthead_url = 'http://media.wacotrib.com/designimages/wacotrib_logo.jpg' + keep_only_tags = [ + dict(name='div', attrs={'class':'twoColumn left'}), + ] + remove_tags = [ + dict(name='div', attrs={'class':'right blueLinks'}), + ] + remove_tags_after = [ + dict(name='div', attrs={'class':'dottedRule'}), + ] From 01d5dcff6fbeeb1bb24bce807962743cdf8d9e9c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 14 Jul 2010 09:03:20 -0600 Subject: [PATCH 28/38] Support for another variant of the Samsung Galaxy --- src/calibre/devices/android/driver.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 9951dc57ad..5d9d094b26 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -30,7 +30,8 @@ class ANDROID(USBMS): 0x18d1 : { 0x4e11 : [0x0100, 0x226], 0x4e12: [0x0100, 0x226]}, # Samsung - 0x04e8 : { 0x681d : [0x0222, 0x0400], 0x681c : [0x0222, 0x0224]}, + 0x04e8 : { 0x681d : [0x0222, 0x0400], + 0x681c : [0x0222, 0x0224, 0x0400]}, # Acer 0x502 : { 0x3203 : [0x0100]}, From 447afcdd08f1c40b7def917f50f3c8c57a314d6f Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 15 Jul 2010 21:28:52 +0100 Subject: [PATCH 29/38] Fix cache problem where merging metadata did not values explicitly set to None, such as erasing series. --- src/calibre/devices/usbms/books.py | 2 +- src/calibre/ebooks/metadata/__init__.py | 14 +++++++++----- src/calibre/gui2/device.py | 15 ++++++++++----- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py index 6394626a9f..5ad4ef4723 100644 --- a/src/calibre/devices/usbms/books.py +++ b/src/calibre/devices/usbms/books.py @@ -78,7 +78,7 @@ class Book(MetaInformation): in C{other} takes precedence, unless the information in C{other} is NULL. ''' - MetaInformation.smart_update(self, other, replace_tags=True) + MetaInformation.smart_update(self, other, replace_metadata=True) for attr in self.BOOK_ATTRS: if hasattr(other, attr): diff --git a/src/calibre/ebooks/metadata/__init__.py b/src/calibre/ebooks/metadata/__init__.py index 0dbffd5f7f..d5e7aafb32 100644 --- a/src/calibre/ebooks/metadata/__init__.py +++ b/src/calibre/ebooks/metadata/__init__.py @@ -268,10 +268,12 @@ class MetaInformation(object): ): prints(x, getattr(self, x, 'None')) - def smart_update(self, mi, replace_tags=False): + def smart_update(self, mi, replace_metadata=False): ''' - Merge the information in C{mi} into self. In case of conflicts, the information - in C{mi} takes precedence, unless the information in mi is NULL. + Merge the information in C{mi} into self. In case of conflicts, the + information in C{mi} takes precedence, unless the information in mi is + NULL. If replace_metadata is True, then the information in mi always + takes precedence. ''' if mi.title and mi.title != _('Unknown'): self.title = mi.title @@ -285,13 +287,15 @@ class MetaInformation(object): 'cover', 'guide', 'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate', 'rights', 'publication_type', 'uuid'): - if hasattr(mi, attr): + if replace_metadata: + setattr(self, attr, getattr(mi, attr, None)) + elif hasattr(mi, attr): val = getattr(mi, attr) if val is not None: setattr(self, attr, val) if mi.tags: - if replace_tags: + if replace_metadata: self.tags = mi.tags else: self.tags += mi.tags diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index d81918c307..bc8ba7c381 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -1439,7 +1439,8 @@ class DeviceMixin(object): # {{{ for book in booklist: if getattr(book, 'uuid', None) in self.db_book_uuid_cache: if update_metadata: - book.smart_update(self.db_book_uuid_cache[book.uuid]) + book.smart_update(self.db_book_uuid_cache[book.uuid], + replace_metadata=True) book.in_library = True # ensure that the correct application_id is set book.application_id = \ @@ -1454,12 +1455,14 @@ class DeviceMixin(object): # {{{ if getattr(book, 'application_id', None) in d['db_ids']: book.in_library = True if update_metadata: - book.smart_update(d['db_ids'][book.application_id]) + book.smart_update(d['db_ids'][book.application_id], + replace_metadata=True) continue if book.db_id in d['db_ids']: book.in_library = True if update_metadata: - book.smart_update(d['db_ids'][book.db_id]) + book.smart_update(d['db_ids'][book.db_id], + replace_metadata=True) continue if book.authors: # Compare against both author and author sort, because @@ -1469,11 +1472,13 @@ class DeviceMixin(object): # {{{ if book_authors in d['authors']: book.in_library = True if update_metadata: - book.smart_update(d['authors'][book_authors]) + book.smart_update(d['authors'][book_authors], + replace_metadata=True) elif book_authors in d['author_sort']: book.in_library = True if update_metadata: - book.smart_update(d['author_sort'][book_authors]) + book.smart_update(d['author_sort'][book_authors], + replace_metadata=True) # Set author_sort if it isn't already asort = getattr(book, 'author_sort', None) if not asort and book.authors: From 17c9b2f206825d166cf1775eb6e1e5ef304c88f1 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 15 Jul 2010 23:19:09 +0100 Subject: [PATCH 30/38] Fix missing replace_metadata parameter --- src/calibre/devices/usbms/books.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py index 5ad4ef4723..cdba980642 100644 --- a/src/calibre/devices/usbms/books.py +++ b/src/calibre/devices/usbms/books.py @@ -72,13 +72,13 @@ class Book(MetaInformation): def thumbnail(self): return None - def smart_update(self, other): + def smart_update(self, other, replace_metadata=False): ''' Merge the information in C{other} into self. In case of conflicts, the information in C{other} takes precedence, unless the information in C{other} is NULL. ''' - MetaInformation.smart_update(self, other, replace_metadata=True) + MetaInformation.smart_update(self, other, replace_metadata) for attr in self.BOOK_ATTRS: if hasattr(other, attr): @@ -116,7 +116,7 @@ class BookList(_BookList): self.append(book) return True if replace_metadata: - self[b].smart_update(book) + self[b].smart_update(book, replace_metadata=True) return True return False From fcdc320a33dc33c80641491b50556dd7d9ce535c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 16 Jul 2010 01:07:40 -0600 Subject: [PATCH 31/38] Orlando Sentinel by rty --- resources/recipes/orlando_sentinel.recipe | 38 +++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 resources/recipes/orlando_sentinel.recipe diff --git a/resources/recipes/orlando_sentinel.recipe b/resources/recipes/orlando_sentinel.recipe new file mode 100644 index 0000000000..7a59f6f6ba --- /dev/null +++ b/resources/recipes/orlando_sentinel.recipe @@ -0,0 +1,38 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class AdvancedUserRecipe1279258912(BasicNewsRecipe): + title = u'Orlando Sentinel' + oldest_article = 3 + max_articles_per_feed = 100 + + feeds = [ + (u'News', u'http://feeds.feedburner.com/orlandosentinel/news'), + (u'Opinion', u'http://feeds.feedburner.com/orlandosentinel/news/opinion'), + (u'Business', u'http://feeds.feedburner.com/orlandosentinel/business'), + (u'Technology', u'http://feeds.feedburner.com/orlandosentinel/technology'), + (u'Space and Science', u'http://feeds.feedburner.com/orlandosentinel/news/space'), + (u'Entertainment', u'http://feeds.feedburner.com/orlandosentinel/entertainment'), + (u'Life and Family', u'http://feeds.feedburner.com/orlandosentinel/features/lifestyle'), + ] + __author__ = 'rty' + pubisher = 'OrlandoSentinel.com' + description = 'Orlando, Florida, Newspaper' + category = 'News, Orlando, Florida' + + + remove_javascript = True + use_embedded_content = False + no_stylesheets = True + language = 'en' + encoding = 'utf-8' + conversion_options = {'linearize_tables':True} + masthead_url = 'http://www.orlandosentinel.com/media/graphic/2009-07/46844851.gif' + keep_only_tags = [ + dict(name='div', attrs={'class':'story'}) + ] + remove_tags = [ + dict(name='div', attrs={'class':['articlerail','tools','comment-group','clearfix']}), + ] + remove_tags_after = [ + dict(name='p', attrs={'class':'copyright'}), + ] From 452083dc346ab04f28076751141bfdd1af091fd8 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Fri, 16 Jul 2010 09:53:29 +0100 Subject: [PATCH 32/38] Fix eject button --- src/calibre/gui2/layout.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index 0228249e8d..c1ff3ab505 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -138,7 +138,7 @@ class LocationModel(QAbstractListModel): # {{{ class LocationView(QListView): - unmount_device = pyqtSignal() + umount_device = pyqtSignal() location_selected = pyqtSignal(object) def __init__(self, parent): @@ -174,7 +174,7 @@ class LocationView(QListView): self.setMinimumWidth(400) def eject_clicked(self, *args): - self.unmount_device.emit() + self.umount_device.emit() def count_changed(self, new_count): self.model().count = new_count From 5e4af6a58fabf94fd350940b98b947c75032321b Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Fri, 16 Jul 2010 13:00:10 +0100 Subject: [PATCH 33/38] Fix for #6193 --- src/calibre/gui2/convert/regex_builder.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/calibre/gui2/convert/regex_builder.py b/src/calibre/gui2/convert/regex_builder.py index 6fa0fa5fe4..b10772b86c 100644 --- a/src/calibre/gui2/convert/regex_builder.py +++ b/src/calibre/gui2/convert/regex_builder.py @@ -28,9 +28,10 @@ class RegexBuilder(QDialog, Ui_RegexBuilder): if not db or not book_id: self.button_box.addButton(QDialogButtonBox.Open) - else: - self.select_format(db, book_id) - + elif not self.select_format(db, book_id): + self.cancelled = True + return + self.cancelled = False self.connect(self.button_box, SIGNAL('clicked(QAbstractButton*)'), self.button_clicked) self.connect(self.regex, SIGNAL('textChanged(QString)'), self.regex_valid) self.connect(self.test, SIGNAL('clicked()'), self.do_test) @@ -79,10 +80,12 @@ class RegexBuilder(QDialog, Ui_RegexBuilder): format = d.format() if not format: - error_dialog(self, _('No formats available'), _('Cannot build regex using the GUI builder without a book.')) - QDialog.reject() - else: - self.open_book(db.format_abspath(book_id, format, index_is_id=True)) + error_dialog(self, _('No formats available'), + _('Cannot build regex using the GUI builder without a book.'), + show=True) + return False + self.open_book(db.format_abspath(book_id, format, index_is_id=True)) + return True def open_book(self, pathtoebook): self.iterator = EbookIterator(pathtoebook) @@ -117,6 +120,8 @@ class RegexEdit(QWidget, Ui_Edit): def builder(self): bld = RegexBuilder(self.db, self.book_id, self.edit.text(), self) + if bld.cancelled: + return if bld.exec_() == bld.Accepted: self.edit.setText(bld.regex.text()) From f197f916a806770608d9200c470cbe20ee839eae Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 16 Jul 2010 08:46:47 -0600 Subject: [PATCH 34/38] Fix #5060 (When converting .rtf superscript format is lost) --- resources/templates/rtf.xsl | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/resources/templates/rtf.xsl b/resources/templates/rtf.xsl index 9199654665..74696f0857 100644 --- a/resources/templates/rtf.xsl +++ b/resources/templates/rtf.xsl @@ -111,7 +111,6 @@ or (@shadow = 'true') or (@hidden = 'true') or (@outline = 'true') - "> @@ -277,6 +276,26 @@ ] + + + + + + + + + + + + + + + + + + + + From 5c5bd4d1db1a62b81a10f82e56a1f39006458409 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 16 Jul 2010 10:15:56 -0600 Subject: [PATCH 35/38] Support for the MiBuk --- src/calibre/devices/jetbook/driver.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/calibre/devices/jetbook/driver.py b/src/calibre/devices/jetbook/driver.py index 671fea5d75..6a3bc635ff 100644 --- a/src/calibre/devices/jetbook/driver.py +++ b/src/calibre/devices/jetbook/driver.py @@ -80,3 +80,21 @@ class JETBOOK(USBMS): return mi +class MIBUK(USBMS): + + name = 'MiBuk Wolder Device Interface' + description = _('Communicate with the MiBuk Wolder reader.') + author = 'Kovid Goyal' + supported_platforms = ['windows', 'osx', 'linux'] + + FORMATS = ['epub', 'mobi', 'prc', 'fb2', 'txt', 'rtf', 'pdf'] + + VENDOR_ID = [0x0525] + PRODUCT_ID = [0xa4a5] + BCD = [0x314] + SUPPORTS_SUB_DIRS = True + + VENDOR_NAME = 'LINUX' + WINDOWS_MAIN_MEM = 'WOLDERMIBUK' + + From 77e7bf8d06402a52185e6f6a6c1b8a0540b4dbaa Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 16 Jul 2010 10:19:31 -0600 Subject: [PATCH 36/38] Implement #5724 (BibTex Export) --- src/calibre/__init__.py | 2 + src/calibre/customize/builtins.py | 7 +- src/calibre/gui2/catalog/catalog_bibtex.py | 84 + src/calibre/gui2/catalog/catalog_bibtex.ui | 173 ++ src/calibre/library/catalog.py | 360 ++- src/calibre/utils/bibtex.py | 2539 ++++++++++++++++++++ 6 files changed, 3146 insertions(+), 19 deletions(-) create mode 100644 src/calibre/gui2/catalog/catalog_bibtex.py create mode 100644 src/calibre/gui2/catalog/catalog_bibtex.ui create mode 100644 src/calibre/utils/bibtex.py diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 92ee2ca6d2..15bd54c80c 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -361,6 +361,8 @@ def strftime(fmt, t=None): before 1900 ''' if t is None: t = time.localtime() + if hasattr(t, 'timetuple'): + t = t.timetuple() early_year = t[0] < 1900 if early_year: replacement = 1900 if t[0]%4 == 0 else 1901 diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 9d876b42d1..35cb0ad3d2 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -446,7 +446,7 @@ from calibre.devices.eb600.driver import EB600, COOL_ER, SHINEBOOK, \ BOOQ, ELONEX, POCKETBOOK301, MENTOR from calibre.devices.iliad.driver import ILIAD from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800 -from calibre.devices.jetbook.driver import JETBOOK +from calibre.devices.jetbook.driver import JETBOOK, MIBUK from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX from calibre.devices.nook.driver import NOOK from calibre.devices.prs505.driver import PRS505 @@ -467,12 +467,12 @@ from calibre.devices.kobo.driver import KOBO from calibre.ebooks.metadata.fetch import GoogleBooks, ISBNDB, Amazon, \ LibraryThing from calibre.ebooks.metadata.douban import DoubanBooks -from calibre.library.catalog import CSV_XML, EPUB_MOBI +from calibre.library.catalog import CSV_XML, EPUB_MOBI, BIBTEX from calibre.ebooks.epub.fix.unmanifested import Unmanifested from calibre.ebooks.epub.fix.epubcheck import Epubcheck plugins = [HTML2ZIP, PML2PMLZ, ArchiveExtract, GoogleBooks, ISBNDB, Amazon, - LibraryThing, DoubanBooks, CSV_XML, EPUB_MOBI, Unmanifested, Epubcheck] + LibraryThing, DoubanBooks, CSV_XML, EPUB_MOBI, BIBTEX, Unmanifested, Epubcheck] plugins += [ ComicInput, EPUBInput, @@ -517,6 +517,7 @@ plugins += [ IREXDR1000, IREXDR800, JETBOOK, + MIBUK, SHINEBOOK, POCKETBOOK360, POCKETBOOK301, diff --git a/src/calibre/gui2/catalog/catalog_bibtex.py b/src/calibre/gui2/catalog/catalog_bibtex.py new file mode 100644 index 0000000000..ea222603b7 --- /dev/null +++ b/src/calibre/gui2/catalog/catalog_bibtex.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import with_statement + +__license__ = 'GPL v3' +__copyright__ = '2009, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + +from calibre.gui2 import gprefs +from calibre.gui2.catalog.catalog_bibtex_ui import Ui_Form +from PyQt4.Qt import QWidget, QListWidgetItem + +class PluginWidget(QWidget, Ui_Form): + + TITLE = _('BibTeX Options') + HELP = _('Options specific to')+' BibTeX '+_('output') + OPTION_FIELDS = [('bib_cit','{authors}{id}'), + ('bib_entry', 0), #mixed + ('bibfile_enc', 0), #utf-8 + ('bibfile_enctag', 0), #strict + ('impcit', True) ] + + sync_enabled = False + formats = set(['bib']) + + def __init__(self, parent=None): + QWidget.__init__(self, parent) + self.setupUi(self) + from calibre.library.catalog import FIELDS + self.all_fields = [] + for x in FIELDS : + if x != 'all': + self.all_fields.append(x) + QListWidgetItem(x, self.db_fields) + + def initialize(self, name): #not working properly to update + self.name = name + fields = gprefs.get(name+'_db_fields', self.all_fields) + # Restore the activated db_fields from last use + for x in xrange(self.db_fields.count()): + item = self.db_fields.item(x) + item.setSelected(unicode(item.text()) in fields) + # Update dialog fields from stored options + for opt in self.OPTION_FIELDS: + opt_value = gprefs.get(self.name + '_' + opt[0], opt[1]) + if opt[0] in ['bibfile_enc', 'bibfile_enctag', 'bib_entry']: + getattr(self, opt[0]).setCurrentIndex(opt_value) + elif opt[0] == 'impcit' : + getattr(self, opt[0]).setChecked(opt_value) + else: + getattr(self, opt[0]).setText(opt_value) + + def options(self): + + # Save the currently activated fields + fields = [] + for x in xrange(self.db_fields.count()): + item = self.db_fields.item(x) + if item.isSelected(): + fields.append(unicode(item.text())) + gprefs.set(self.name+'_db_fields', fields) + + # Dictionary currently activated fields + if len(self.db_fields.selectedItems()): + opts_dict = {'fields':[unicode(item.text()) for item in self.db_fields.selectedItems()]} + else: + opts_dict = {'fields':['all']} + + # Save/return the current options + # bib_cit stores as text + # 'bibfile_enc','bibfile_enctag' stores as int (Indexes) + for opt in self.OPTION_FIELDS: + if opt[0] in ['bibfile_enc', 'bibfile_enctag', 'bib_entry']: + opt_value = getattr(self,opt[0]).currentIndex() + elif opt[0] == 'impcit' : + opt_value = getattr(self, opt[0]).isChecked() + else : + opt_value = unicode(getattr(self, opt[0]).text()) + gprefs.set(self.name + '_' + opt[0], opt_value) + + opts_dict[opt[0]] = opt_value + + return opts_dict diff --git a/src/calibre/gui2/catalog/catalog_bibtex.ui b/src/calibre/gui2/catalog/catalog_bibtex.ui new file mode 100644 index 0000000000..7f4920655d --- /dev/null +++ b/src/calibre/gui2/catalog/catalog_bibtex.ui @@ -0,0 +1,173 @@ + + + Form + + + + 0 + 0 + 579 + 411 + + + + Form + + + + + + Bib file encoding: + + + + + + + Fields to include in output: + + + + + + + + utf-8 + + + + + cp1252 + + + + + ascii/LaTeX + + + + + + + + + 0 + 0 + + + + + + + QAbstractItemView::MultiSelection + + + + + + + Encoding configuration (change if you have errors) : + + + + + + + + strict + + + + + replace + + + + + ignore + + + + + backslashreplace + + + + + + + + Qt::Vertical + + + + 20 + 60 + + + + + + + + BibTeX entry type: + + + + + + + + mixed + + + + + misc + + + + + book + + + + + + + + Create a citation tag? + + + + + + + Expression to form the BibTeX citation tag: + + + + + + + + + + Some explanation about this template: + -The fields availables are 'author_sort', 'authors', 'id', + 'isbn', 'pubdate', 'publisher', 'series_index', 'series', + 'tags', 'timestamp', 'title', 'uuid' + -For list types ie authors and tags, only the first element + wil be selected. + -For time field, only the date will be used. + + + false + + + + + + + + diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 21aa863031..a540a8a660 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -1,7 +1,9 @@ +# -*- coding: utf-8 -*- + __license__ = 'GPL v3' __copyright__ = '2010, Greg Riker ' -import datetime, htmlentitydefs, os, re, shutil +import datetime, htmlentitydefs, os, re, shutil, codecs from collections import namedtuple from copy import deepcopy @@ -9,6 +11,7 @@ from copy import deepcopy from xml.sax.saxutils import escape from calibre import filesystem_encoding, prints, prepare_string_for_xml, strftime +from calibre.constants import preferred_encoding from calibre.customize import CatalogPlugin from calibre.customize.conversion import OptionRecommendation, DummyReporter from calibre.ebooks.BeautifulSoup import BeautifulSoup, BeautifulStoneSoup, Tag, NavigableString @@ -21,6 +24,10 @@ FIELDS = ['all', 'author_sort', 'authors', 'comments', 'series_index', 'series', 'size', 'tags', 'timestamp', 'title', 'uuid'] +#Allowed fields for template +TEMPLATE_ALLOWED_FIELDS = [ 'author_sort', 'authors', 'id', 'isbn', 'pubdate', + 'publisher', 'series_index', 'series', 'tags', 'timestamp', 'title', 'uuid' ] + class CSV_XML(CatalogPlugin): 'CSV/XML catalog generator' @@ -89,17 +96,20 @@ class CSV_XML(CatalogPlugin): fields = self.get_output_fields(opts) if self.fmt == 'csv': - outfile = open(path_to_output, 'w') + outfile = codecs.open(path_to_output, 'w', 'utf8') # Output the field headers outfile.write(u'%s\n' % u','.join(fields)) # Output the entry fields for entry in data: - outstr = '' - for (x, field) in enumerate(fields): + outstr = [] + for field in fields: item = entry[field] - if field == 'formats': + if item is None: + outstr.append('""') + continue + elif field == 'formats': fmt_list = [] for format in item: fmt_list.append(format.rpartition('.')[2].lower()) @@ -111,18 +121,13 @@ class CSV_XML(CatalogPlugin): item = u'%s' % re.sub(r'[\D]', '', item) elif field in ['pubdate', 'timestamp']: item = isoformat(item) + elif field == 'comments': + item = item.replace(u'\r\n',u' ') + item = item.replace(u'\n',u' ') - if x < len(fields) - 1: - if item is not None: - outstr += u'"%s",' % unicode(item).replace('"','""') - else: - outstr += '"",' - else: - if item is not None: - outstr += u'"%s"\n' % unicode(item).replace('"','""') - else: - outstr += '""\n' - outfile.write(outstr.encode('utf-8')) + outstr.append(u'"%s"' % unicode(item).replace('"','""')) + + outfile.write(u','.join(outstr) + u'\n') outfile.close() elif self.fmt == 'xml': @@ -181,6 +186,329 @@ class CSV_XML(CatalogPlugin): f.write(etree.tostring(root, encoding='utf-8', xml_declaration=True, pretty_print=True)) +class BIBTEX(CatalogPlugin): + 'BIBTEX catalog generator' + + Option = namedtuple('Option', 'option, default, dest, action, help') + + name = 'Catalog_BIBTEX' + description = 'BIBTEX catalog generator' + supported_platforms = ['windows', 'osx', 'linux'] + author = 'Sengian' + version = (1, 0, 0) + file_types = set(['bib']) + + cli_options = [ + Option('--fields', + default = 'all', + dest = 'fields', + action = None, + help = _('The fields to output when cataloging books in the ' + 'database. Should be a comma-separated list of fields.\n' + 'Available fields: %s.\n' + "Default: '%%default'\n" + "Applies to: BIBTEX output format")%', '.join(FIELDS)), + + Option('--sort-by', + default = 'id', + dest = 'sort_by', + action = None, + help = _('Output field to sort on.\n' + 'Available fields: author_sort, id, rating, size, timestamp, title.\n' + "Default: '%default'\n" + "Applies to: BIBTEX output format")), + + Option('--create-citation', + default = 'True', + dest = 'impcit', + action = None, + help = _('Create a citation for BibTeX entries.\n' + 'Boolean value: True, False\n' + "Default: '%default'\n" + "Applies to: BIBTEX output format")), + + Option('--citation-template', + default = '{authors}{id}', + dest = 'bib_cit', + action = None, + help = _('The template for citation creation from database fields.\n' + ' Should be a template with {} enclosed fields.\n' + 'Available fields: %s.\n' + "Default: '%%default'\n" + "Applies to: BIBTEX output format")%', '.join(TEMPLATE_ALLOWED_FIELDS)), + + Option('--choose-encoding', + default = 'utf8', + dest = 'bibfile_enc', + action = None, + help = _('BibTeX file encoding output.\n' + 'Available types: utf8, cp1252, ascii.\n' + "Default: '%default'\n" + "Applies to: BIBTEX output format")), + + Option('--choose-encoding-configuration', + default = 'strict', + dest = 'bibfile_enctag', + action = None, + help = _('BibTeX file encoding flag.\n' + 'Available types: strict, replace, ignore, backslashreplace.\n' + "Default: '%default'\n" + "Applies to: BIBTEX output format")), + + Option('--entry-type', + default = 'book', + dest = 'bib_entry', + action = None, + help = _('Entry type for BibTeX catalog.\n' + 'Available types: book, misc, mixed.\n' + "Default: '%default'\n" + "Applies to: BIBTEX output format"))] + + def run(self, path_to_output, opts, db, notification=DummyReporter()): + + from types import StringType, UnicodeType + + from calibre.library.save_to_disk import preprocess_template + #Bibtex functions + from calibre.utils.bibtex import bibtex_author_format, utf8ToBibtex, ValidateCitationKey + + def create_bibtex_entry(entry, fields, mode, template_citation, + asccii_bibtex = True, citation_bibtex = True): + + #Bibtex doesn't like UTF-8 but keep unicode until writing + #Define starting chain or if book valid strict and not book return a Fail string + + bibtex_entry = [] + if mode != "misc" and check_entry_book_valid(entry) : + bibtex_entry.append(u'@book{') + elif mode != "book" : + bibtex_entry.append(u'@misc{') + else : + #case strict book + return '' + + if citation_bibtex : + # Citation tag + bibtex_entry.append(make_bibtex_citation(entry, template_citation, asccii_bibtex)) + bibtex_entry = [u' '.join(bibtex_entry)] + + for field in fields: + item = entry[field] + #check if the field should be included (none or empty) + if item is None: + continue + try: + if len(item) == 0 : + continue + except TypeError: + pass + + if field == 'authors' : + bibtex_entry.append(u'author = "%s"' % bibtex_author_format(item)) + + elif field in ['title', 'publisher', 'cover', 'uuid', + 'author_sort', 'series'] : + bibtex_entry.append(u'%s = "%s"' % (field, utf8ToBibtex(item, asccii_bibtex))) + + elif field == 'id' : + bibtex_entry.append(u'calibreid = "%s"' % int(item)) + + elif field == 'rating' : + bibtex_entry.append(u'rating = "%s"' % int(item)) + + elif field == 'size' : + bibtex_entry.append(u'%s = "%s octets"' % (field, int(item))) + + elif field == 'tags' : + #A list to flatten + bibtex_entry.append(u'tags = "%s"' % utf8ToBibtex(u', '.join(item), asccii_bibtex)) + + elif field == 'comments' : + #\n removal + item = item.replace(u'\r\n',u' ') + item = item.replace(u'\n',u' ') + bibtex_entry.append(u'note = "%s"' % utf8ToBibtex(item, asccii_bibtex)) + + elif field == 'isbn' : + # Could be 9, 10 or 13 digits + bibtex_entry.append(u'isbn = "%s"' % re.sub(u'[\D]', u'', item)) + + elif field == 'formats' : + item = u', '.join([format.rpartition('.')[2].lower() for format in item]) + bibtex_entry.append(u'formats = "%s"' % item) + + elif field == 'series_index' : + bibtex_entry.append(u'volume = "%s"' % int(item)) + + elif field == 'timestamp' : + bibtex_entry.append(u'timestamp = "%s"' % isoformat(item).partition('T')[0]) + + elif field == 'pubdate' : + bibtex_entry.append(u'year = "%s"' % item.year) + bibtex_entry.append(u'month = "%s"' % utf8ToBibtex(strftime("%b", item), + asccii_bibtex)) + + bibtex_entry = u',\n '.join(bibtex_entry) + bibtex_entry += u' }\n\n' + + return bibtex_entry + + def check_entry_book_valid(entry): + #Check that the required fields are ok for a book entry + for field in ['title', 'authors', 'publisher'] : + if entry[field] is None or len(entry[field]) == 0 : + return False + if entry['pubdate'] is None : + return False + else : + return True + + def make_bibtex_citation(entry, template_citation, asccii_bibtex): + + #define a function to replace the template entry by its value + def tpl_replace(objtplname) : + + tpl_field = re.sub(u'[\{\}]', u'', objtplname.group()) + + if tpl_field in TEMPLATE_ALLOWED_FIELDS : + if tpl_field in ['pubdate', 'timestamp'] : + tpl_field = isoformat(entry[tpl_field]).partition('T')[0] + elif tpl_field in ['tags', 'authors'] : + tpl_field =entry[tpl_field][0] + elif tpl_field in ['id', 'series_index'] : + tpl_field = str(entry[tpl_field]) + else : + tpl_field = entry[tpl_field] + return tpl_field + else: + return u'' + + if len(template_citation) >0 : + tpl_citation = utf8ToBibtex(ValidateCitationKey(re.sub(u'\{[^{}]*\}', + tpl_replace, template_citation)), asccii_bibtex) + + if len(tpl_citation) >0 : + return tpl_citation + + if len(entry["isbn"]) > 0 : + template_citation = u'%s' % re.sub(u'[\D]',u'', entry["isbn"]) + + else : + template_citation = u'%s' % str(entry["id"]) + + if asccii_bibtex : + return ValidateCitationKey(template_citation.encode('ascii', 'replace')) + else : + return ValidateCitationKey(template_citation) + + self.fmt = path_to_output.rpartition('.')[2] + self.notification = notification + + # Combobox options + bibfile_enc = ['utf8', 'cp1252', 'ascii'] + bibfile_enctag = ['strict', 'replace', 'ignore', 'backslashreplace'] + bib_entry = ['mixed', 'misc', 'book'] + + # Needed beacause CLI return str vs int by widget + try: + bibfile_enc = bibfile_enc[opts.bibfile_enc] + bibfile_enctag = bibfile_enctag[opts.bibfile_enctag] + bib_entry = bib_entry[opts.bib_entry] + except: + if opts.bibfile_enc in bibfile_enc : + bibfile_enc = opts.bibfile_enc + else : + log(" WARNING: incorrect --choose-encoding flag, revert to default") + bibfile_enc = bibfile_enc[0] + if opts.bibfile_enctag in bibfile_enctag : + bibfile_enctag = opts.bibfile_enctag + else : + log(" WARNING: incorrect --choose-encoding-configuration flag, revert to default") + bibfile_enctag = bibfile_enctag[0] + if opts.bib_entry in bib_entry : + bib_entry = opts.bib_entry + else : + log(" WARNING: incorrect --entry-type flag, revert to default") + bib_entry = bib_entry[0] + + if opts.verbose: + opts_dict = vars(opts) + log("%s(): Generating %s" % (self.name,self.fmt)) + if opts_dict['search_text']: + log(" --search='%s'" % opts_dict['search_text']) + + if opts_dict['ids']: + log(" Book count: %d" % len(opts_dict['ids'])) + if opts_dict['search_text']: + log(" (--search ignored when a subset of the database is specified)") + + if opts_dict['fields']: + if opts_dict['fields'] == 'all': + log(" Fields: %s" % ', '.join(FIELDS[1:])) + else: + log(" Fields: %s" % opts_dict['fields']) + + log(" Output file will be encoded in %s with %s flag" % (bibfile_enc, bibfile_enctag)) + + log(" BibTeX entry type is %s with a citation like '%s' flag" % (bib_entry, opts_dict['bib_cit'])) + + # If a list of ids are provided, don't use search_text + if opts.ids: + opts.search_text = None + + data = self.search_sort_db(db, opts) + + if not len(data): + log.error("\nNo matching database entries for search criteria '%s'" % opts.search_text) + + # Get the requested output fields as a list + fields = self.get_output_fields(opts) + + if not len(data): + log.error("\nNo matching database entries for search criteria '%s'" % opts.search_text) + + #Entries writing after Bibtex formating (or not) + if bibfile_enc != 'ascii' : + asccii_bibtex = False + else : + asccii_bibtex = True + + #Check and go to default in case of bad CLI + if isinstance(opts.impcit, (StringType, UnicodeType)) : + if opts.impcit == 'False' : + citation_bibtex= False + elif opts.impcit == 'True' : + citation_bibtex= True + else : + log(" WARNING: incorrect --create-citation, revert to default") + citation_bibtex= True + else : + citation_bibtex= opts.impcit + + template_citation = preprocess_template(opts.bib_cit) + + #Open output and write entries + outfile = codecs.open(path_to_output, 'w', bibfile_enc, bibfile_enctag) + + #File header + nb_entries = len(data) + + #check in book strict if all is ok else throw a warning into log + if bib_entry == 'book' : + nb_books = len(filter(check_entry_book_valid, data)) + if nb_books < nb_entries : + log(" WARNING: only %d entries in %d are book compatible" % (nb_books, nb_entries)) + nb_entries = nb_books + + outfile.write(u'%%%Calibre catalog\n%%%{0} entries in catalog\n\n'.format(nb_entries)) + outfile.write(u'@preamble{"This catalog of %d entries was generated by calibre on %s"}\n\n' + % (nb_entries, nowf().strftime("%A, %d. %B %Y %H:%M").decode(preferred_encoding))) + + for entry in data: + outfile.write(create_bibtex_entry(entry, fields, bib_entry, template_citation, + asccii_bibtex, citation_bibtex)) + + outfile.close() class EPUB_MOBI(CatalogPlugin): 'ePub catalog generator' diff --git a/src/calibre/utils/bibtex.py b/src/calibre/utils/bibtex.py new file mode 100644 index 0000000000..f4fc62d9d0 --- /dev/null +++ b/src/calibre/utils/bibtex.py @@ -0,0 +1,2539 @@ +# -*- coding: utf-8 -*- + +""" Collection of python utility-methodes commonly used by other + bibliograph packages. + From http://pypi.python.org/pypi/bibliograph.core/ + from Tom Gross + + Adapted for calibre use + + Zope Public License (ZPL) Version 2.1 + + A copyright notice accompanies this license document that + identifies the copyright holders. + + This license has been certified as open source. It has also + been designated as GPL compatible by the Free Software + Foundation (FSF). + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the + following conditions are met: + + 1. Redistributions in source code must retain the + accompanying copyright notice, this list of conditions, + and the following disclaimer. + + 2. Redistributions in binary form must reproduce the accompanying + copyright notice, this list of conditions, and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + + 3. Names of the copyright holders must not be used to + endorse or promote products derived from this software + without prior written permission from the copyright + holders. + + 4. The right to distribute this software or to use it for + any purpose does not give you the right to use + Servicemarks (sm) or Trademarks (tm) of the copyright + holders. Use of them is covered by separate agreement + with the copyright holders. + + 5. If any files are modified, you must cause the modified + files to carry prominent notices stating that you changed + the files and the date of any change. + + Disclaimer + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' + AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT + NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + NO EVENT SHALL THE COPYRIGHT HOLDERS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. + """ + +__docformat__ = 'reStructuredText' +__author__ = 'sengian ' + +import os, re, string + +utf8enc2latex_mapping = { + # This is a mapping of Unicode characters to LaTeX equivalents. + # The information has been extracted from + # , written by + # David Carlisle and Sebastian Rahtz. + # + # The extraction has been done by the "create_unimap.py" script + # located at . + + #Fix some encoding problem between cp1252 and latin1 + # from http://www.microsoft.com/typography/unicode/1252.htm + u'\x80': '{\\mbox{\\texteuro}}', # EURO SIGN + u'\x82': '{,}', # SINGLE LOW-9 QUOTATION MARK + u'\x83': '$f$', # LATIN SMALL LETTER F WITH HOOK + u'\x84': '{,,}', # DOUBLE LOW-9 QUOTATION MARK + u'\x85': '{\\ldots}', # HORIZONTAL ELLIPSIS + u'\x86': '{\\textdagger}', # DAGGER + u'\x87': '{\\textdaggerdbl}', # DOUBLE DAGGER + u'\x88': '{\textasciicircum}', # MODIFIER LETTER CIRCUMFLEX ACCENT + u'\x89': '{\\textperthousand}', # PER MILLE SIGN + u'\x8A': '{\\v{S}}', # LATIN CAPITAL LETTER S WITH CARON + u'\x8B': '{\\guilsinglleft}', # SINGLE LEFT-POINTING ANGLE QUOTATION MARK + u'\x8C': '{\\OE}', # LATIN CAPITAL LIGATURE OE + u'\x8E': '{\\v{Z}}', # LATIN CAPITAL LETTER Z WITH CARON + u'\x91': '{`}', # LEFT SINGLE QUOTATION MARK + u'\x92': "{'}", # RIGHT SINGLE QUOTATION MARK + u'\x93': '{\\textquotedblleft}', # LEFT DOUBLE QUOTATION MARK + u'\x94': '{\\textquotedblright}', # RIGHT DOUBLE QUOTATION MARK + u'\x95': '{\\textbullet}', # BULLET + u'\x96': '{\\textendash}', # EN DASH + u'\x97': '{\\textemdash}', # EM DASH + u'\x98': '{\\texttildelow}', # SMALL TILDE + u'\x99': '{\\texttrademark}', # TRADE MARK SIGN + u'\x9A': '{\\v{s}}', # LATIN SMALL LETTER S WITH CARON + u'\x9B': '{\\guilsinglright}', # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + u'\x9C': '{\\oe}', # LATIN SMALL LIGATURE OE + u'\x9E': '{\\v{z}}', # LATIN SMALL LETTER Z WITH CARON + u'\x9F': '{\\"{Y}}', # LATIN CAPITAL LETTER Y WITH DIAERESIS + + u'\xa0': '$~$', + u'\xa1': '{\\textexclamdown}', + u'\xa2': '{\\textcent}', + u'\xa3': '{\\textsterling}', + u'\xa4': '{\\textcurrency}', + u'\xa5': '{\\textyen}', + u'\xa6': '{\\textbrokenbar}', + u'\xa7': '{\\textsection}', + u'\xa8': '{\\textasciidieresis}', + u'\xa9': '{\\textcopyright}', + u'\xaa': '{\\textordfeminine}', + u'\xab': '{\\guillemotleft}', + u'\xac': '$\\lnot$', + u'\xad': '$\\-$', + u'\xae': '{\\textregistered}', + u'\xaf': '{\\textasciimacron}', + u'\xb0': '{\\textdegree}', + u'\xb1': '$\\pm$', + u'\xb2': '${^2}$', + u'\xb3': '${^3}$', + u'\xb4': '{\\textasciiacute}', + u'\xb5': '$\\mathrm{\\mu}$', + u'\xb6': '{\\textparagraph}', + u'\xb7': '$\\cdot$', + u'\xb8': '{\\c{}}', + u'\xb9': '${^1}$', + u'\xba': '{\\textordmasculine}', + u'\xbb': '{\\guillemotright}', + u'\xbc': '{\\textonequarter}', + u'\xbd': '{\\textonehalf}', + u'\xbe': '{\\textthreequarters}', + u'\xbf': '{\\textquestiondown}', + u'\xc0': '{\\`{A}}', + u'\xc1': "{\\'{A}}", + u'\xc2': '{\\^{A}}', + u'\xc3': '{\\~{A}}', + u'\xc4': '{\\"{A}}', + u'\xc5': '{\\AA}', + u'\xc6': '{\\AE}', + u'\xc7': '{\\c{C}}', + u'\xc8': '{\\`{E}}', + u'\xc9': "{\\'{E}}", + u'\xca': '{\\^{E}}', + u'\xcb': '{\\"{E}}', + u'\xcc': '{\\`{I}}', + u'\xcd': "{\\'{I}}", + u'\xce': '{\\^{I}}', + u'\xcf': '{\\"{I}}', + u'\xd0': '{\\DH}', + u'\xd1': '{\\~{N}}', + u'\xd2': '{\\`{O}}', + u'\xd3': "{\\'{O}}", + u'\xd4': '{\\^{O}}', + u'\xd5': '{\\~{O}}', + u'\xd6': '{\\"{O}}', + u'\xd7': '{\\texttimes}', + u'\xd8': '{\\O}', + u'\xd9': '{\\`{U}}', + u'\xda': "{\\'{U}}", + u'\xdb': '{\\^{U}}', + u'\xdc': '{\\"{U}}', + u'\xdd': "{\\'{Y}}", + u'\xde': '{\\TH}', + u'\xdf': '{\\ss}', + u'\xe0': '{\\`{a}}', + u'\xe1': "{\\'{a}}", + u'\xe2': '{\\^{a}}', + u'\xe3': '{\\~{a}}', + u'\xe4': '{\\"{a}}', + u'\xe5': '{\\aa}', + u'\xe6': '{\\ae}', + u'\xe7': '{\\c{c}}', + u'\xe8': '{\\`{e}}', + u'\xe9': "{\\'{e}}", + u'\xea': '{\\^{e}}', + u'\xeb': '{\\"{e}}', + u'\xec': '{\\`{\\i}}', + u'\xed': "{\\'{\\i}}", + u'\xee': '{\\^{\\i}}', + u'\xef': '{\\"{\\i}}', + u'\xf0': '{\\dh}', + u'\xf1': '{\\~{n}}', + u'\xf2': '{\\`{o}}', + u'\xf3': "{\\'{o}}", + u'\xf4': '{\\^{o}}', + u'\xf5': '{\\~{o}}', + u'\xf6': '{\\"{o}}', + u'\xf7': '$\\div$', + u'\xf8': '{\\o}', + u'\xf9': '{\\`{u}}', + u'\xfa': "{\\'{u}}", + u'\xfb': '{\\^{u}}', + u'\xfc': '{\\"{u}}', + u'\xfd': "{\\'{y}}", + u'\xfe': '{\\th}', + u'\xff': '{\\"{y}}', + u'\u0100': '{\\={A}}', + u'\u0101': '{\\={a}}', + u'\u0102': '{\\u{A}}', + u'\u0103': '{\\u{a}}', + u'\u0104': '{\\k{A}}', + u'\u0105': '{\\k{a}}', + u'\u0106': "{\\'{C}}", + u'\u0107': "{\\'{c}}", + u'\u0108': '{\\^{C}}', + u'\u0109': '{\\^{c}}', + u'\u010a': '{\\.{C}}', + u'\u010b': '{\\.{c}}', + u'\u010c': '{\\v{C}}', + u'\u010d': '{\\v{c}}', + u'\u010e': '{\\v{D}}', + u'\u010f': '{\\v{d}}', + u'\u0110': '{\\DJ}', + u'\u0111': '{\\dj}', + u'\u0112': '{\\={E}}', + u'\u0113': '{\\={e}}', + u'\u0114': '{\\u{E}}', + u'\u0115': '{\\u{e}}', + u'\u0116': '{\\.{E}}', + u'\u0117': '{\\.{e}}', + u'\u0118': '{\\k{E}}', + u'\u0119': '{\\k{e}}', + u'\u011a': '{\\v{E}}', + u'\u011b': '{\\v{e}}', + u'\u011c': '{\\^{G}}', + u'\u011d': '{\\^{g}}', + u'\u011e': '{\\u{G}}', + u'\u011f': '{\\u{g}}', + u'\u0120': '{\\.{G}}', + u'\u0121': '{\\.{g}}', + u'\u0122': '{\\c{G}}', + u'\u0123': '{\\c{g}}', + u'\u0124': '{\\^{H}}', + u'\u0125': '{\\^{h}}', + u'\u0126': '{{\\fontencoding{LELA}\\selectfont\\char40}}', + u'\u0127': '$\\Elzxh$', + u'\u0128': '{\\~{I}}', + u'\u0129': '{\\~{\\i}}', + u'\u012a': '{\\={I}}', + u'\u012b': '{\\={\\i}}', + u'\u012c': '{\\u{I}}', + u'\u012d': '{\\u{\\i}}', + u'\u012e': '{\\k{I}}', + u'\u012f': '{\\k{i}}', + u'\u0130': '{\\.{I}}', + u'\u0131': '{\\i}', + u'\u0132': '{IJ}', + u'\u0133': '{ij}', + u'\u0134': '{\\^{J}}', + u'\u0135': '{\\^{\\j}}', + u'\u0136': '{\\c{K}}', + u'\u0137': '{\\c{k}}', + u'\u0138': '{{\\fontencoding{LELA}\\selectfont\\char91}}', + u'\u0139': "{\\'{L}}", + u'\u013a': "{\\'{l}}", + u'\u013b': '{\\c{L}}', + u'\u013c': '{\\c{l}}', + u'\u013d': '{\\v{L}}', + u'\u013e': '{\\v{l}}', + u'\u013f': '{{\\fontencoding{LELA}\\selectfont\\char201}}', + u'\u0140': '{{\\fontencoding{LELA}\\selectfont\\char202}}', + u'\u0141': '{\\L}', + u'\u0142': '{\\l}', + u'\u0143': "{\\'{N}}", + u'\u0144': "{\\'{n}}", + u'\u0145': '{\\c{N}}', + u'\u0146': '{\\c{n}}', + u'\u0147': '{\\v{N}}', + u'\u0148': '{\\v{n}}', + u'\u0149': "{'n}", + u'\u014a': '{\\NG}', + u'\u014b': '{\\ng}', + u'\u014c': '{\\={O}}', + u'\u014d': '{\\={o}}', + u'\u014e': '{\\u{O}}', + u'\u014f': '{\\u{o}}', + u'\u0150': '{\\H{O}}', + u'\u0151': '{\\H{o}}', + u'\u0152': '{\\OE}', + u'\u0153': '{\\oe}', + u'\u0154': "{\\'{R}}", + u'\u0155': "{\\'{r}}", + u'\u0156': '{\\c{R}}', + u'\u0157': '{\\c{r}}', + u'\u0158': '{\\v{R}}', + u'\u0159': '{\\v{r}}', + u'\u015a': "{\\'{S}}", + u'\u015b': "{\\'{s}}", + u'\u015c': '{\\^{S}}', + u'\u015d': '{\\^{s}}', + u'\u015e': '{\\c{S}}', + u'\u015f': '{\\c{s}}', + u'\u0160': '{\\v{S}}', + u'\u0161': '{\\v{s}}', + u'\u0162': '{\\c{T}}', + u'\u0163': '{\\c{t}}', + u'\u0164': '{\\v{T}}', + u'\u0165': '{\\v{t}}', + u'\u0166': '{{\\fontencoding{LELA}\\selectfont\\char47}}', + u'\u0167': '{{\\fontencoding{LELA}\\selectfont\\char63}}', + u'\u0168': '{\\~{U}}', + u'\u0169': '{\\~{u}}', + u'\u016a': '{\\={U}}', + u'\u016b': '{\\={u}}', + u'\u016c': '{\\u{U}}', + u'\u016d': '{\\u{u}}', + u'\u016e': '{\\r{U}}', + u'\u016f': '{\\r{u}}', + u'\u0170': '{\\H{U}}', + u'\u0171': '{\\H{u}}', + u'\u0172': '{\\k{U}}', + u'\u0173': '{\\k{u}}', + u'\u0174': '{\\^{W}}', + u'\u0175': '{\\^{w}}', + u'\u0176': '{\\^{Y}}', + u'\u0177': '{\\^{y}}', + u'\u0178': '{\\"{Y}}', + u'\u0179': "{\\'{Z}}", + u'\u017a': "{\\'{z}}", + u'\u017b': '{\\.{Z}}', + u'\u017c': '{\\.{z}}', + u'\u017d': '{\\v{Z}}', + u'\u017e': '{\\v{z}}', + u'\u0192': '$f$', + u'\u0195': '{\\texthvlig}', + u'\u019e': '{\\textnrleg}', + u'\u01aa': '$\\eth$', + u'\u01ba': '{{\\fontencoding{LELA}\\selectfont\\char195}}', + u'\u01c2': '{\\textdoublepipe}', + u'\u01f5': "{\\'{g}}", + u'\u0250': '$\\Elztrna$', + u'\u0252': '$\\Elztrnsa$', + u'\u0254': '$\\Elzopeno$', + u'\u0256': '$\\Elzrtld$', + u'\u0258': '{{\\fontencoding{LEIP}\\selectfont\\char61}}', + u'\u0259': '$\\Elzschwa$', + u'\u025b': '$\\varepsilon$', + u'\u0261': '{g}', + u'\u0263': '$\\Elzpgamma$', + u'\u0264': '$\\Elzpbgam$', + u'\u0265': '$\\Elztrnh$', + u'\u026c': '$\\Elzbtdl$', + u'\u026d': '$\\Elzrtll$', + u'\u026f': '$\\Elztrnm$', + u'\u0270': '$\\Elztrnmlr$', + u'\u0271': '$\\Elzltlmr$', + u'\u0272': '{\\Elzltln}', + u'\u0273': '$\\Elzrtln$', + u'\u0277': '$\\Elzclomeg$', + u'\u0278': '{\\textphi}', + u'\u0279': '$\\Elztrnr$', + u'\u027a': '$\\Elztrnrl$', + u'\u027b': '$\\Elzrttrnr$', + u'\u027c': '$\\Elzrl$', + u'\u027d': '$\\Elzrtlr$', + u'\u027e': '$\\Elzfhr$', + u'\u027f': '{{\\fontencoding{LEIP}\\selectfont\\char202}}', + u'\u0282': '$\\Elzrtls$', + u'\u0283': '$\\Elzesh$', + u'\u0287': '$\\Elztrnt$', + u'\u0288': '$\\Elzrtlt$', + u'\u028a': '$\\Elzpupsil$', + u'\u028b': '$\\Elzpscrv$', + u'\u028c': '$\\Elzinvv$', + u'\u028d': '$\\Elzinvw$', + u'\u028e': '$\\Elztrny$', + u'\u0290': '$\\Elzrtlz$', + u'\u0292': '$\\Elzyogh$', + u'\u0294': '$\\Elzglst$', + u'\u0295': '$\\Elzreglst$', + u'\u0296': '$\\Elzinglst$', + u'\u029e': '{\\textturnk}', + u'\u02a4': '$\\Elzdyogh$', + u'\u02a7': '$\\Elztesh$', + u'\u02bc': "{'}", + u'\u02c7': '{\\textasciicaron}', + u'\u02c8': '$\\Elzverts$', + u'\u02cc': '$\\Elzverti$', + u'\u02d0': '$\\Elzlmrk$', + u'\u02d1': '$\\Elzhlmrk$', + u'\u02d2': '$\\Elzsbrhr$', + u'\u02d3': '$\\Elzsblhr$', + u'\u02d4': '$\\Elzrais$', + u'\u02d5': '$\\Elzlow$', + u'\u02d8': '{\\textasciibreve}', + u'\u02d9': '{\\textperiodcentered}', + u'\u02da': '{\\r{}}', + u'\u02db': '{\\k{}}', + u'\u02dc': '{\\texttildelow}', + u'\u02dd': '{\\H{}}', + u'\u02e5': '{\\tone{55}}', + u'\u02e6': '{\\tone{44}}', + u'\u02e7': '{\\tone{33}}', + u'\u02e8': '{\\tone{22}}', + u'\u02e9': '{\\tone{11}}', + u'\u0300': '{\\`}', + u'\u0301': "{\\'}", + u'\u0302': '{\\^}', + u'\u0303': '{\\~}', + u'\u0304': '{\\=}', + u'\u0306': '{\\u}', + u'\u0307': '{\\.}', + u'\u0308': '{\\"}', + u'\u030a': '{\\r}', + u'\u030b': '{\\H}', + u'\u030c': '{\\v}', + u'\u030f': '{\\cyrchar\\C}', + u'\u0311': '{{\\fontencoding{LECO}\\selectfont\\char177}}', + u'\u0318': '{{\\fontencoding{LECO}\\selectfont\\char184}}', + u'\u0319': '{{\\fontencoding{LECO}\\selectfont\\char185}}', + u'\u0321': '$\\Elzpalh$', + u'\u0322': '{\\Elzrh}', + u'\u0327': '{\\c}', + u'\u0328': '{\\k}', + u'\u032a': '$\\Elzsbbrg$', + u'\u032b': '{{\\fontencoding{LECO}\\selectfont\\char203}}', + u'\u032f': '{{\\fontencoding{LECO}\\selectfont\\char207}}', + u'\u0335': '{\\Elzxl}', + u'\u0336': '{\\Elzbar}', + u'\u0337': '{{\\fontencoding{LECO}\\selectfont\\char215}}', + u'\u0338': '{{\\fontencoding{LECO}\\selectfont\\char216}}', + u'\u033a': '{{\\fontencoding{LECO}\\selectfont\\char218}}', + u'\u033b': '{{\\fontencoding{LECO}\\selectfont\\char219}}', + u'\u033c': '{{\\fontencoding{LECO}\\selectfont\\char220}}', + u'\u033d': '{{\\fontencoding{LECO}\\selectfont\\char221}}', + u'\u0361': '{{\\fontencoding{LECO}\\selectfont\\char225}}', + u'\u0386': "{\\'{A}}", + u'\u0388': "{\\'{E}}", + u'\u0389': "{\\'{H}}", + u'\u038a': "{\\'{}{I}}", + u'\u038c': "{\\'{}O}", + u'\u038e': "$\\mathrm{'Y}$", + u'\u038f': "$\\mathrm{'\\Omega}$", + u'\u0390': '$\\acute{\\ddot{\\iota}}$', + u'\u0391': '$\\Alpha$', + u'\u0392': '$\\Beta$', + u'\u0393': '$\\Gamma$', + u'\u0394': '$\\Delta$', + u'\u0395': '$\\Epsilon$', + u'\u0396': '$\\Zeta$', + u'\u0397': '$\\Eta$', + u'\u0398': '$\\Theta$', + u'\u0399': '$\\Iota$', + u'\u039a': '$\\Kappa$', + u'\u039b': '$\\Lambda$', + u'\u039c': '$M$', + u'\u039d': '$N$', + u'\u039e': '$\\Xi$', + u'\u039f': '$O$', + u'\u03a0': '$\\Pi$', + u'\u03a1': '$\\Rho$', + u'\u03a3': '$\\Sigma$', + u'\u03a4': '$\\Tau$', + u'\u03a5': '$\\Upsilon$', + u'\u03a6': '$\\Phi$', + u'\u03a7': '$\\Chi$', + u'\u03a8': '$\\Psi$', + u'\u03a9': '$\\Omega$', + u'\u03aa': '$\\mathrm{\\ddot{I}}$', + u'\u03ab': '$\\mathrm{\\ddot{Y}}$', + u'\u03ac': "{\\'{$\\alpha$}}", + u'\u03ad': '$\\acute{\\epsilon}$', + u'\u03ae': '$\\acute{\\eta}$', + u'\u03af': '$\\acute{\\iota}$', + u'\u03b0': '$\\acute{\\ddot{\\upsilon}}$', + u'\u03b1': '$\\alpha$', + u'\u03b2': '$\\beta$', + u'\u03b3': '$\\gamma$', + u'\u03b4': '$\\delta$', + u'\u03b5': '$\\epsilon$', + u'\u03b6': '$\\zeta$', + u'\u03b7': '$\\eta$', + u'\u03b8': '{\\texttheta}', + u'\u03b9': '$\\iota$', + u'\u03ba': '$\\kappa$', + u'\u03bb': '$\\lambda$', + u'\u03bc': '$\\mu$', + u'\u03bd': '$\\nu$', + u'\u03be': '$\\xi$', + u'\u03bf': '$o$', + u'\u03c0': '$\\pi$', + u'\u03c1': '$\\rho$', + u'\u03c2': '$\\varsigma$', + u'\u03c3': '$\\sigma$', + u'\u03c4': '$\\tau$', + u'\u03c5': '$\\upsilon$', + u'\u03c6': '$\\varphi$', + u'\u03c7': '$\\chi$', + u'\u03c8': '$\\psi$', + u'\u03c9': '$\\omega$', + u'\u03ca': '$\\ddot{\\iota}$', + u'\u03cb': '$\\ddot{\\upsilon}$', + u'\u03cc': "{\\'{o}}", + u'\u03cd': '$\\acute{\\upsilon}$', + u'\u03ce': '$\\acute{\\omega}$', + u'\u03d0': '{\\Pisymbol{ppi022}{87}}', + u'\u03d1': '{\\textvartheta}', + u'\u03d2': '$\\Upsilon$', + u'\u03d5': '$\\phi$', + u'\u03d6': '$\\varpi$', + u'\u03da': '$\\Stigma$', + u'\u03dc': '$\\Digamma$', + u'\u03dd': '$\\digamma$', + u'\u03de': '$\\Koppa$', + u'\u03e0': '$\\Sampi$', + u'\u03f0': '$\\varkappa$', + u'\u03f1': '$\\varrho$', + u'\u03f4': '{\\textTheta}', + u'\u03f6': '$\\backepsilon$', + u'\u0401': '{\\cyrchar\\CYRYO}', + u'\u0402': '{\\cyrchar\\CYRDJE}', + u'\u0403': "{\\cyrchar{\\'\\CYRG}}", + u'\u0404': '{\\cyrchar\\CYRIE}', + u'\u0405': '{\\cyrchar\\CYRDZE}', + u'\u0406': '{\\cyrchar\\CYRII}', + u'\u0407': '{\\cyrchar\\CYRYI}', + u'\u0408': '{\\cyrchar\\CYRJE}', + u'\u0409': '{\\cyrchar\\CYRLJE}', + u'\u040a': '{\\cyrchar\\CYRNJE}', + u'\u040b': '{\\cyrchar\\CYRTSHE}', + u'\u040c': "{\\cyrchar{\\'\\CYRK}}", + u'\u040e': '{\\cyrchar\\CYRUSHRT}', + u'\u040f': '{\\cyrchar\\CYRDZHE}', + u'\u0410': '{\\cyrchar\\CYRA}', + u'\u0411': '{\\cyrchar\\CYRB}', + u'\u0412': '{\\cyrchar\\CYRV}', + u'\u0413': '{\\cyrchar\\CYRG}', + u'\u0414': '{\\cyrchar\\CYRD}', + u'\u0415': '{\\cyrchar\\CYRE}', + u'\u0416': '{\\cyrchar\\CYRZH}', + u'\u0417': '{\\cyrchar\\CYRZ}', + u'\u0418': '{\\cyrchar\\CYRI}', + u'\u0419': '{\\cyrchar\\CYRISHRT}', + u'\u041a': '{\\cyrchar\\CYRK}', + u'\u041b': '{\\cyrchar\\CYRL}', + u'\u041c': '{\\cyrchar\\CYRM}', + u'\u041d': '{\\cyrchar\\CYRN}', + u'\u041e': '{\\cyrchar\\CYRO}', + u'\u041f': '{\\cyrchar\\CYRP}', + u'\u0420': '{\\cyrchar\\CYRR}', + u'\u0421': '{\\cyrchar\\CYRS}', + u'\u0422': '{\\cyrchar\\CYRT}', + u'\u0423': '{\\cyrchar\\CYRU}', + u'\u0424': '{\\cyrchar\\CYRF}', + u'\u0425': '{\\cyrchar\\CYRH}', + u'\u0426': '{\\cyrchar\\CYRC}', + u'\u0427': '{\\cyrchar\\CYRCH}', + u'\u0428': '{\\cyrchar\\CYRSH}', + u'\u0429': '{\\cyrchar\\CYRSHCH}', + u'\u042a': '{\\cyrchar\\CYRHRDSN}', + u'\u042b': '{\\cyrchar\\CYRERY}', + u'\u042c': '{\\cyrchar\\CYRSFTSN}', + u'\u042d': '{\\cyrchar\\CYREREV}', + u'\u042e': '{\\cyrchar\\CYRYU}', + u'\u042f': '{\\cyrchar\\CYRYA}', + u'\u0430': '{\\cyrchar\\cyra}', + u'\u0431': '{\\cyrchar\\cyrb}', + u'\u0432': '{\\cyrchar\\cyrv}', + u'\u0433': '{\\cyrchar\\cyrg}', + u'\u0434': '{\\cyrchar\\cyrd}', + u'\u0435': '{\\cyrchar\\cyre}', + u'\u0436': '{\\cyrchar\\cyrzh}', + u'\u0437': '{\\cyrchar\\cyrz}', + u'\u0438': '{\\cyrchar\\cyri}', + u'\u0439': '{\\cyrchar\\cyrishrt}', + u'\u043a': '{\\cyrchar\\cyrk}', + u'\u043b': '{\\cyrchar\\cyrl}', + u'\u043c': '{\\cyrchar\\cyrm}', + u'\u043d': '{\\cyrchar\\cyrn}', + u'\u043e': '{\\cyrchar\\cyro}', + u'\u043f': '{\\cyrchar\\cyrp}', + u'\u0440': '{\\cyrchar\\cyrr}', + u'\u0441': '{\\cyrchar\\cyrs}', + u'\u0442': '{\\cyrchar\\cyrt}', + u'\u0443': '{\\cyrchar\\cyru}', + u'\u0444': '{\\cyrchar\\cyrf}', + u'\u0445': '{\\cyrchar\\cyrh}', + u'\u0446': '{\\cyrchar\\cyrc}', + u'\u0447': '{\\cyrchar\\cyrch}', + u'\u0448': '{\\cyrchar\\cyrsh}', + u'\u0449': '{\\cyrchar\\cyrshch}', + u'\u044a': '{\\cyrchar\\cyrhrdsn}', + u'\u044b': '{\\cyrchar\\cyrery}', + u'\u044c': '{\\cyrchar\\cyrsftsn}', + u'\u044d': '{\\cyrchar\\cyrerev}', + u'\u044e': '{\\cyrchar\\cyryu}', + u'\u044f': '{\\cyrchar\\cyrya}', + u'\u0451': '{\\cyrchar\\cyryo}', + u'\u0452': '{\\cyrchar\\cyrdje}', + u'\u0453': "{\\cyrchar{\\'\\cyrg}}", + u'\u0454': '{\\cyrchar\\cyrie}', + u'\u0455': '{\\cyrchar\\cyrdze}', + u'\u0456': '{\\cyrchar\\cyrii}', + u'\u0457': '{\\cyrchar\\cyryi}', + u'\u0458': '{\\cyrchar\\cyrje}', + u'\u0459': '{\\cyrchar\\cyrlje}', + u'\u045a': '{\\cyrchar\\cyrnje}', + u'\u045b': '{\\cyrchar\\cyrtshe}', + u'\u045c': "{\\cyrchar{\\'\\cyrk}}", + u'\u045e': '{\\cyrchar\\cyrushrt}', + u'\u045f': '{\\cyrchar\\cyrdzhe}', + u'\u0460': '{\\cyrchar\\CYROMEGA}', + u'\u0461': '{\\cyrchar\\cyromega}', + u'\u0462': '{\\cyrchar\\CYRYAT}', + u'\u0464': '{\\cyrchar\\CYRIOTE}', + u'\u0465': '{\\cyrchar\\cyriote}', + u'\u0466': '{\\cyrchar\\CYRLYUS}', + u'\u0467': '{\\cyrchar\\cyrlyus}', + u'\u0468': '{\\cyrchar\\CYRIOTLYUS}', + u'\u0469': '{\\cyrchar\\cyriotlyus}', + u'\u046a': '{\\cyrchar\\CYRBYUS}', + u'\u046c': '{\\cyrchar\\CYRIOTBYUS}', + u'\u046d': '{\\cyrchar\\cyriotbyus}', + u'\u046e': '{\\cyrchar\\CYRKSI}', + u'\u046f': '{\\cyrchar\\cyrksi}', + u'\u0470': '{\\cyrchar\\CYRPSI}', + u'\u0471': '{\\cyrchar\\cyrpsi}', + u'\u0472': '{\\cyrchar\\CYRFITA}', + u'\u0474': '{\\cyrchar\\CYRIZH}', + u'\u0478': '{\\cyrchar\\CYRUK}', + u'\u0479': '{\\cyrchar\\cyruk}', + u'\u047a': '{\\cyrchar\\CYROMEGARND}', + u'\u047b': '{\\cyrchar\\cyromegarnd}', + u'\u047c': '{\\cyrchar\\CYROMEGATITLO}', + u'\u047d': '{\\cyrchar\\cyromegatitlo}', + u'\u047e': '{\\cyrchar\\CYROT}', + u'\u047f': '{\\cyrchar\\cyrot}', + u'\u0480': '{\\cyrchar\\CYRKOPPA}', + u'\u0481': '{\\cyrchar\\cyrkoppa}', + u'\u0482': '{\\cyrchar\\cyrthousands}', + u'\u0488': '{\\cyrchar\\cyrhundredthousands}', + u'\u0489': '{\\cyrchar\\cyrmillions}', + u'\u048c': '{\\cyrchar\\CYRSEMISFTSN}', + u'\u048d': '{\\cyrchar\\cyrsemisftsn}', + u'\u048e': '{\\cyrchar\\CYRRTICK}', + u'\u048f': '{\\cyrchar\\cyrrtick}', + u'\u0490': '{\\cyrchar\\CYRGUP}', + u'\u0491': '{\\cyrchar\\cyrgup}', + u'\u0492': '{\\cyrchar\\CYRGHCRS}', + u'\u0493': '{\\cyrchar\\cyrghcrs}', + u'\u0494': '{\\cyrchar\\CYRGHK}', + u'\u0495': '{\\cyrchar\\cyrghk}', + u'\u0496': '{\\cyrchar\\CYRZHDSC}', + u'\u0497': '{\\cyrchar\\cyrzhdsc}', + u'\u0498': '{\\cyrchar\\CYRZDSC}', + u'\u0499': '{\\cyrchar\\cyrzdsc}', + u'\u049a': '{\\cyrchar\\CYRKDSC}', + u'\u049b': '{\\cyrchar\\cyrkdsc}', + u'\u049c': '{\\cyrchar\\CYRKVCRS}', + u'\u049d': '{\\cyrchar\\cyrkvcrs}', + u'\u049e': '{\\cyrchar\\CYRKHCRS}', + u'\u049f': '{\\cyrchar\\cyrkhcrs}', + u'\u04a0': '{\\cyrchar\\CYRKBEAK}', + u'\u04a1': '{\\cyrchar\\cyrkbeak}', + u'\u04a2': '{\\cyrchar\\CYRNDSC}', + u'\u04a3': '{\\cyrchar\\cyrndsc}', + u'\u04a4': '{\\cyrchar\\CYRNG}', + u'\u04a5': '{\\cyrchar\\cyrng}', + u'\u04a6': '{\\cyrchar\\CYRPHK}', + u'\u04a7': '{\\cyrchar\\cyrphk}', + u'\u04a8': '{\\cyrchar\\CYRABHHA}', + u'\u04a9': '{\\cyrchar\\cyrabhha}', + u'\u04aa': '{\\cyrchar\\CYRSDSC}', + u'\u04ab': '{\\cyrchar\\cyrsdsc}', + u'\u04ac': '{\\cyrchar\\CYRTDSC}', + u'\u04ad': '{\\cyrchar\\cyrtdsc}', + u'\u04ae': '{\\cyrchar\\CYRY}', + u'\u04af': '{\\cyrchar\\cyry}', + u'\u04b0': '{\\cyrchar\\CYRYHCRS}', + u'\u04b1': '{\\cyrchar\\cyryhcrs}', + u'\u04b2': '{\\cyrchar\\CYRHDSC}', + u'\u04b3': '{\\cyrchar\\cyrhdsc}', + u'\u04b4': '{\\cyrchar\\CYRTETSE}', + u'\u04b5': '{\\cyrchar\\cyrtetse}', + u'\u04b6': '{\\cyrchar\\CYRCHRDSC}', + u'\u04b7': '{\\cyrchar\\cyrchrdsc}', + u'\u04b8': '{\\cyrchar\\CYRCHVCRS}', + u'\u04b9': '{\\cyrchar\\cyrchvcrs}', + u'\u04ba': '{\\cyrchar\\CYRSHHA}', + u'\u04bb': '{\\cyrchar\\cyrshha}', + u'\u04bc': '{\\cyrchar\\CYRABHCH}', + u'\u04bd': '{\\cyrchar\\cyrabhch}', + u'\u04be': '{\\cyrchar\\CYRABHCHDSC}', + u'\u04bf': '{\\cyrchar\\cyrabhchdsc}', + u'\u04c0': '{\\cyrchar\\CYRpalochka}', + u'\u04c3': '{\\cyrchar\\CYRKHK}', + u'\u04c4': '{\\cyrchar\\cyrkhk}', + u'\u04c7': '{\\cyrchar\\CYRNHK}', + u'\u04c8': '{\\cyrchar\\cyrnhk}', + u'\u04cb': '{\\cyrchar\\CYRCHLDSC}', + u'\u04cc': '{\\cyrchar\\cyrchldsc}', + u'\u04d4': '{\\cyrchar\\CYRAE}', + u'\u04d5': '{\\cyrchar\\cyrae}', + u'\u04d8': '{\\cyrchar\\CYRSCHWA}', + u'\u04d9': '{\\cyrchar\\cyrschwa}', + u'\u04e0': '{\\cyrchar\\CYRABHDZE}', + u'\u04e1': '{\\cyrchar\\cyrabhdze}', + u'\u04e8': '{\\cyrchar\\CYROTLD}', + u'\u04e9': '{\\cyrchar\\cyrotld}', + u'\u2002': '{\\hspace{0.6em}}', + u'\u2003': '{\\hspace{1em}}', + u'\u2004': '{\\hspace{0.33em}}', + u'\u2005': '{\\hspace{0.25em}}', + u'\u2006': '{\\hspace{0.166em}}', + u'\u2007': '{\\hphantom{0}}', + u'\u2008': '{\\hphantom{,}}', + u'\u2009': '{\\hspace{0.167em}}', + u'\u200a': '$\\mkern1mu$', + u'\u2010': '{-}', + u'\u2013': '{\\textendash}', + u'\u2014': '{\\textemdash}', + u'\u2015': '{\\rule{1em}{1pt}}', + u'\u2016': '$\\Vert$', + u'\u2018': '{`}', + u'\u2019': "{'}", + u'\u201a': '{,}', + u'\u201b': '$\\Elzreapos$', + u'\u201c': '{\\textquotedblleft}', + u'\u201d': '{\\textquotedblright}', + u'\u201e': '{,,}', + u'\u2020': '{\\textdagger}', + u'\u2021': '{\\textdaggerdbl}', + u'\u2022': '{\\textbullet}', + u'\u2024': '{.}', + u'\u2025': '{..}', + u'\u2026': '{\\ldots}', + u'\u2030': '{\\textperthousand}', + u'\u2031': '{\\textpertenthousand}', + u'\u2032': "${'}$", + u'\u2033': "${''}$", + u'\u2034': "${'''}$", + u'\u2035': '$\\backprime$', + u'\u2039': '{\\guilsinglleft}', + u'\u203a': '{\\guilsinglright}', + u'\u2057': "$''''$", + u'\u205f': '{\\mkern4mu}', + u'\u2060': '{\\nolinebreak}', + u'\u20a7': '{\\ensuremath{\\Elzpes}}', + u'\u20ac': '{\\mbox{\\texteuro}}', + u'\u20db': '$\\dddot$', + u'\u20dc': '$\\ddddot$', + u'\u2102': '$\\mathbb{C}$', + u'\u210a': '{\\mathscr{g}}', + u'\u210b': '$\\mathscr{H}$', + u'\u210c': '$\\mathfrak{H}$', + u'\u210d': '$\\mathbb{H}$', + u'\u210f': '$\\hslash$', + u'\u2110': '$\\mathscr{I}$', + u'\u2111': '$\\mathfrak{I}$', + u'\u2112': '$\\mathscr{L}$', + u'\u2113': '$\\mathscr{l}$', + u'\u2115': '$\\mathbb{N}$', + u'\u2116': '{\\cyrchar\\textnumero}', + u'\u2118': '$\\wp$', + u'\u2119': '$\\mathbb{P}$', + u'\u211a': '$\\mathbb{Q}$', + u'\u211b': '$\\mathscr{R}$', + u'\u211c': '$\\mathfrak{R}$', + u'\u211d': '$\\mathbb{R}$', + u'\u211e': '$\\Elzxrat$', + u'\u2122': '{\\texttrademark}', + u'\u2124': '$\\mathbb{Z}$', + u'\u2126': '$\\Omega$', + u'\u2127': '$\\mho$', + u'\u2128': '$\\mathfrak{Z}$', + u'\u2129': '$\\ElsevierGlyph{2129}$', + u'\u212b': '{\\AA}', + u'\u212c': '$\\mathscr{B}$', + u'\u212d': '$\\mathfrak{C}$', + u'\u212f': '$\\mathscr{e}$', + u'\u2130': '$\\mathscr{E}$', + u'\u2131': '$\\mathscr{F}$', + u'\u2133': '$\\mathscr{M}$', + u'\u2134': '$\\mathscr{o}$', + u'\u2135': '$\\aleph$', + u'\u2136': '$\\beth$', + u'\u2137': '$\\gimel$', + u'\u2138': '$\\daleth$', + u'\u2153': '$\\textfrac{1}{3}$', + u'\u2154': '$\\textfrac{2}{3}$', + u'\u2155': '$\\textfrac{1}{5}$', + u'\u2156': '$\\textfrac{2}{5}$', + u'\u2157': '$\\textfrac{3}{5}$', + u'\u2158': '$\\textfrac{4}{5}$', + u'\u2159': '$\\textfrac{1}{6}$', + u'\u215a': '$\\textfrac{5}{6}$', + u'\u215b': '$\\textfrac{1}{8}$', + u'\u215c': '$\\textfrac{3}{8}$', + u'\u215d': '$\\textfrac{5}{8}$', + u'\u215e': '$\\textfrac{7}{8}$', + u'\u2190': '$\\leftarrow$', + u'\u2191': '$\\uparrow$', + u'\u2192': '$\\rightarrow$', + u'\u2193': '$\\downarrow$', + u'\u2194': '$\\leftrightarrow$', + u'\u2195': '$\\updownarrow$', + u'\u2196': '$\\nwarrow$', + u'\u2197': '$\\nearrow$', + u'\u2198': '$\\searrow$', + u'\u2199': '$\\swarrow$', + u'\u219a': '$\\nleftarrow$', + u'\u219b': '$\\nrightarrow$', + u'\u219c': '$\\arrowwaveright$', + u'\u219d': '$\\arrowwaveright$', + u'\u219e': '$\\twoheadleftarrow$', + u'\u21a0': '$\\twoheadrightarrow$', + u'\u21a2': '$\\leftarrowtail$', + u'\u21a3': '$\\rightarrowtail$', + u'\u21a6': '$\\mapsto$', + u'\u21a9': '$\\hookleftarrow$', + u'\u21aa': '$\\hookrightarrow$', + u'\u21ab': '$\\looparrowleft$', + u'\u21ac': '$\\looparrowright$', + u'\u21ad': '$\\leftrightsquigarrow$', + u'\u21ae': '$\\nleftrightarrow$', + u'\u21b0': '$\\Lsh$', + u'\u21b1': '$\\Rsh$', + u'\u21b3': '$\\ElsevierGlyph{21B3}$', + u'\u21b6': '$\\curvearrowleft$', + u'\u21b7': '$\\curvearrowright$', + u'\u21ba': '$\\circlearrowleft$', + u'\u21bb': '$\\circlearrowright$', + u'\u21bc': '$\\leftharpoonup$', + u'\u21bd': '$\\leftharpoondown$', + u'\u21be': '$\\upharpoonright$', + u'\u21bf': '$\\upharpoonleft$', + u'\u21c0': '$\\rightharpoonup$', + u'\u21c1': '$\\rightharpoondown$', + u'\u21c2': '$\\downharpoonright$', + u'\u21c3': '$\\downharpoonleft$', + u'\u21c4': '$\\rightleftarrows$', + u'\u21c5': '$\\dblarrowupdown$', + u'\u21c6': '$\\leftrightarrows$', + u'\u21c7': '$\\leftleftarrows$', + u'\u21c8': '$\\upuparrows$', + u'\u21c9': '$\\rightrightarrows$', + u'\u21ca': '$\\downdownarrows$', + u'\u21cb': '$\\leftrightharpoons$', + u'\u21cc': '$\\rightleftharpoons$', + u'\u21cd': '$\\nLeftarrow$', + u'\u21ce': '$\\nLeftrightarrow$', + u'\u21cf': '$\\nRightarrow$', + u'\u21d0': '$\\Leftarrow$', + u'\u21d1': '$\\Uparrow$', + u'\u21d2': '$\\Rightarrow$', + u'\u21d3': '$\\Downarrow$', + u'\u21d4': '$\\Leftrightarrow$', + u'\u21d5': '$\\Updownarrow$', + u'\u21da': '$\\Lleftarrow$', + u'\u21db': '$\\Rrightarrow$', + u'\u21dd': '$\\rightsquigarrow$', + u'\u21f5': '$\\DownArrowUpArrow$', + u'\u2200': '$\\forall$', + u'\u2201': '$\\complement$', + u'\u2202': '$\\partial$', + u'\u2203': '$\\exists$', + u'\u2204': '$\\nexists$', + u'\u2205': '$\\varnothing$', + u'\u2207': '$\\nabla$', + u'\u2208': '$\\in$', + u'\u2209': '$\\not\\in$', + u'\u220b': '$\\ni$', + u'\u220c': '$\\not\\ni$', + u'\u220f': '$\\prod$', + u'\u2210': '$\\coprod$', + u'\u2211': '$\\sum$', + u'\u2212': '{-}', + u'\u2213': '$\\mp$', + u'\u2214': '$\\dotplus$', + u'\u2216': '$\\setminus$', + u'\u2217': '${_\\ast}$', + u'\u2218': '$\\circ$', + u'\u2219': '$\\bullet$', + u'\u221a': '$\\surd$', + u'\u221d': '$\\propto$', + u'\u221e': '$\\infty$', + u'\u221f': '$\\rightangle$', + u'\u2220': '$\\angle$', + u'\u2221': '$\\measuredangle$', + u'\u2222': '$\\sphericalangle$', + u'\u2223': '$\\mid$', + u'\u2224': '$\\nmid$', + u'\u2225': '$\\parallel$', + u'\u2226': '$\\nparallel$', + u'\u2227': '$\\wedge$', + u'\u2228': '$\\vee$', + u'\u2229': '$\\cap$', + u'\u222a': '$\\cup$', + u'\u222b': '$\\int$', + u'\u222c': '$\\int\\!\\int$', + u'\u222d': '$\\int\\!\\int\\!\\int$', + u'\u222e': '$\\oint$', + u'\u222f': '$\\surfintegral$', + u'\u2230': '$\\volintegral$', + u'\u2231': '$\\clwintegral$', + u'\u2232': '$\\ElsevierGlyph{2232}$', + u'\u2233': '$\\ElsevierGlyph{2233}$', + u'\u2234': '$\\therefore$', + u'\u2235': '$\\because$', + u'\u2237': '$\\Colon$', + u'\u2238': '$\\ElsevierGlyph{2238}$', + u'\u223a': '$\\mathbin{{:}\\!\\!{-}\\!\\!{:}}$', + u'\u223b': '$\\homothetic$', + u'\u223c': '$\\sim$', + u'\u223d': '$\\backsim$', + u'\u223e': '$\\lazysinv$', + u'\u2240': '$\\wr$', + u'\u2241': '$\\not\\sim$', + u'\u2242': '$\\ElsevierGlyph{2242}$', + u'\u2243': '$\\simeq$', + u'\u2244': '$\\not\\simeq$', + u'\u2245': '$\\cong$', + u'\u2246': '$\\approxnotequal$', + u'\u2247': '$\\not\\cong$', + u'\u2248': '$\\approx$', + u'\u2249': '$\\not\\approx$', + u'\u224a': '$\\approxeq$', + u'\u224b': '$\\tildetrpl$', + u'\u224c': '$\\allequal$', + u'\u224d': '$\\asymp$', + u'\u224e': '$\\Bumpeq$', + u'\u224f': '$\\bumpeq$', + u'\u2250': '$\\doteq$', + u'\u2251': '$\\doteqdot$', + u'\u2252': '$\\fallingdotseq$', + u'\u2253': '$\\risingdotseq$', + u'\u2254': '{:=}', + u'\u2255': '$=:$', + u'\u2256': '$\\eqcirc$', + u'\u2257': '$\\circeq$', + u'\u2259': '$\\estimates$', + u'\u225a': '$\\ElsevierGlyph{225A}$', + u'\u225b': '$\\starequal$', + u'\u225c': '$\\triangleq$', + u'\u225f': '$\\ElsevierGlyph{225F}$', + u'\u2260': '$\\not =$', + u'\u2261': '$\\equiv$', + u'\u2262': '$\\not\\equiv$', + u'\u2264': '$\\leq$', + u'\u2265': '$\\geq$', + u'\u2266': '$\\leqq$', + u'\u2267': '$\\geqq$', + u'\u2268': '$\\lneqq$', + u'\u2269': '$\\gneqq$', + u'\u226a': '$\\ll$', + u'\u226b': '$\\gg$', + u'\u226c': '$\\between$', + u'\u226d': '$\\not\\kern-0.3em\\times$', + u'\u226e': '$\\not<$', + u'\u226f': '$\\not>$', + u'\u2270': '$\\not\\leq$', + u'\u2271': '$\\not\\geq$', + u'\u2272': '$\\lessequivlnt$', + u'\u2273': '$\\greaterequivlnt$', + u'\u2274': '$\\ElsevierGlyph{2274}$', + u'\u2275': '$\\ElsevierGlyph{2275}$', + u'\u2276': '$\\lessgtr$', + u'\u2277': '$\\gtrless$', + u'\u2278': '$\\notlessgreater$', + u'\u2279': '$\\notgreaterless$', + u'\u227a': '$\\prec$', + u'\u227b': '$\\succ$', + u'\u227c': '$\\preccurlyeq$', + u'\u227d': '$\\succcurlyeq$', + u'\u227e': '$\\precapprox$', + u'\u227f': '$\\succapprox$', + u'\u2280': '$\\not\\prec$', + u'\u2281': '$\\not\\succ$', + u'\u2282': '$\\subset$', + u'\u2283': '$\\supset$', + u'\u2284': '$\\not\\subset$', + u'\u2285': '$\\not\\supset$', + u'\u2286': '$\\subseteq$', + u'\u2287': '$\\supseteq$', + u'\u2288': '$\\not\\subseteq$', + u'\u2289': '$\\not\\supseteq$', + u'\u228a': '$\\subsetneq$', + u'\u228b': '$\\supsetneq$', + u'\u228e': '$\\uplus$', + u'\u228f': '$\\sqsubset$', + u'\u2290': '$\\sqsupset$', + u'\u2291': '$\\sqsubseteq$', + u'\u2292': '$\\sqsupseteq$', + u'\u2293': '$\\sqcap$', + u'\u2294': '$\\sqcup$', + u'\u2295': '$\\oplus$', + u'\u2296': '$\\ominus$', + u'\u2297': '$\\otimes$', + u'\u2298': '$\\oslash$', + u'\u2299': '$\\odot$', + u'\u229a': '$\\circledcirc$', + u'\u229b': '$\\circledast$', + u'\u229d': '$\\circleddash$', + u'\u229e': '$\\boxplus$', + u'\u229f': '$\\boxminus$', + u'\u22a0': '$\\boxtimes$', + u'\u22a1': '$\\boxdot$', + u'\u22a2': '$\\vdash$', + u'\u22a3': '$\\dashv$', + u'\u22a4': '$\\top$', + u'\u22a5': '$\\perp$', + u'\u22a7': '$\\truestate$', + u'\u22a8': '$\\forcesextra$', + u'\u22a9': '$\\Vdash$', + u'\u22aa': '$\\Vvdash$', + u'\u22ab': '$\\VDash$', + u'\u22ac': '$\\nvdash$', + u'\u22ad': '$\\nvDash$', + u'\u22ae': '$\\nVdash$', + u'\u22af': '$\\nVDash$', + u'\u22b2': '$\\vartriangleleft$', + u'\u22b3': '$\\vartriangleright$', + u'\u22b4': '$\\trianglelefteq$', + u'\u22b5': '$\\trianglerighteq$', + u'\u22b6': '$\\original$', + u'\u22b7': '$\\image$', + u'\u22b8': '$\\multimap$', + u'\u22b9': '$\\hermitconjmatrix$', + u'\u22ba': '$\\intercal$', + u'\u22bb': '$\\veebar$', + u'\u22be': '$\\rightanglearc$', + u'\u22c0': '$\\ElsevierGlyph{22C0}$', + u'\u22c1': '$\\ElsevierGlyph{22C1}$', + u'\u22c2': '$\\bigcap$', + u'\u22c3': '$\\bigcup$', + u'\u22c4': '$\\diamond$', + u'\u22c5': '$\\cdot$', + u'\u22c6': '$\\star$', + u'\u22c7': '$\\divideontimes$', + u'\u22c8': '$\\bowtie$', + u'\u22c9': '$\\ltimes$', + u'\u22ca': '$\\rtimes$', + u'\u22cb': '$\\leftthreetimes$', + u'\u22cc': '$\\rightthreetimes$', + u'\u22cd': '$\\backsimeq$', + u'\u22ce': '$\\curlyvee$', + u'\u22cf': '$\\curlywedge$', + u'\u22d0': '$\\Subset$', + u'\u22d1': '$\\Supset$', + u'\u22d2': '$\\Cap$', + u'\u22d3': '$\\Cup$', + u'\u22d4': '$\\pitchfork$', + u'\u22d6': '$\\lessdot$', + u'\u22d7': '$\\gtrdot$', + u'\u22d8': '$\\verymuchless$', + u'\u22d9': '$\\verymuchgreater$', + u'\u22da': '$\\lesseqgtr$', + u'\u22db': '$\\gtreqless$', + u'\u22de': '$\\curlyeqprec$', + u'\u22df': '$\\curlyeqsucc$', + u'\u22e2': '$\\not\\sqsubseteq$', + u'\u22e3': '$\\not\\sqsupseteq$', + u'\u22e5': '$\\Elzsqspne$', + u'\u22e6': '$\\lnsim$', + u'\u22e7': '$\\gnsim$', + u'\u22e8': '$\\precedesnotsimilar$', + u'\u22e9': '$\\succnsim$', + u'\u22ea': '$\\ntriangleleft$', + u'\u22eb': '$\\ntriangleright$', + u'\u22ec': '$\\ntrianglelefteq$', + u'\u22ed': '$\\ntrianglerighteq$', + u'\u22ee': '$\\vdots$', + u'\u22ef': '$\\cdots$', + u'\u22f0': '$\\upslopeellipsis$', + u'\u22f1': '$\\downslopeellipsis$', + u'\u2305': '{\\barwedge}', + u'\u2306': '$\\perspcorrespond$', + u'\u2308': '$\\lceil$', + u'\u2309': '$\\rceil$', + u'\u230a': '$\\lfloor$', + u'\u230b': '$\\rfloor$', + u'\u2315': '$\\recorder$', + u'\u2316': '$\\mathchar"2208$', + u'\u231c': '$\\ulcorner$', + u'\u231d': '$\\urcorner$', + u'\u231e': '$\\llcorner$', + u'\u231f': '$\\lrcorner$', + u'\u2322': '$\\frown$', + u'\u2323': '$\\smile$', + u'\u2329': '$\\langle$', + u'\u232a': '$\\rangle$', + u'\u233d': '$\\ElsevierGlyph{E838}$', + u'\u23a3': '$\\Elzdlcorn$', + u'\u23b0': '$\\lmoustache$', + u'\u23b1': '$\\rmoustache$', + u'\u2423': '{\\textvisiblespace}', + u'\u2460': '{\\ding{172}}', + u'\u2461': '{\\ding{173}}', + u'\u2462': '{\\ding{174}}', + u'\u2463': '{\\ding{175}}', + u'\u2464': '{\\ding{176}}', + u'\u2465': '{\\ding{177}}', + u'\u2466': '{\\ding{178}}', + u'\u2467': '{\\ding{179}}', + u'\u2468': '{\\ding{180}}', + u'\u2469': '{\\ding{181}}', + u'\u24c8': '$\\circledS$', + u'\u2506': '$\\Elzdshfnc$', + u'\u2519': '$\\Elzsqfnw$', + u'\u2571': '$\\diagup$', + u'\u25a0': '{\\ding{110}}', + u'\u25a1': '$\\square$', + u'\u25aa': '$\\blacksquare$', + u'\u25ad': '$\\fbox{~~}$', + u'\u25af': '$\\Elzvrecto$', + u'\u25b1': '$\\ElsevierGlyph{E381}$', + u'\u25b2': '{\\ding{115}}', + u'\u25b3': '$\\bigtriangleup$', + u'\u25b4': '$\\blacktriangle$', + u'\u25b5': '$\\vartriangle$', + u'\u25b8': '$\\blacktriangleright$', + u'\u25b9': '$\\triangleright$', + u'\u25bc': '{\\ding{116}}', + u'\u25bd': '$\\bigtriangledown$', + u'\u25be': '$\\blacktriangledown$', + u'\u25bf': '$\\triangledown$', + u'\u25c2': '$\\blacktriangleleft$', + u'\u25c3': '$\\triangleleft$', + u'\u25c6': '{\\ding{117}}', + u'\u25ca': '$\\lozenge$', + u'\u25cb': '$\\bigcirc$', + u'\u25cf': '{\\ding{108}}', + u'\u25d0': '$\\Elzcirfl$', + u'\u25d1': '$\\Elzcirfr$', + u'\u25d2': '$\\Elzcirfb$', + u'\u25d7': '{\\ding{119}}', + u'\u25d8': '$\\Elzrvbull$', + u'\u25e7': '$\\Elzsqfl$', + u'\u25e8': '$\\Elzsqfr$', + u'\u25ea': '$\\Elzsqfse$', + u'\u25ef': '$\\bigcirc$', + u'\u2605': '{\\ding{72}}', + u'\u2606': '{\\ding{73}}', + u'\u260e': '{\\ding{37}}', + u'\u261b': '{\\ding{42}}', + u'\u261e': '{\\ding{43}}', + u'\u263e': '{\\rightmoon}', + u'\u263f': '{\\mercury}', + u'\u2640': '{\\venus}', + u'\u2642': '{\\male}', + u'\u2643': '{\\jupiter}', + u'\u2644': '{\\saturn}', + u'\u2645': '{\\uranus}', + u'\u2646': '{\\neptune}', + u'\u2647': '{\\pluto}', + u'\u2648': '{\\aries}', + u'\u2649': '{\\taurus}', + u'\u264a': '{\\gemini}', + u'\u264b': '{\\cancer}', + u'\u264c': '{\\leo}', + u'\u264d': '{\\virgo}', + u'\u264e': '{\\libra}', + u'\u264f': '{\\scorpio}', + u'\u2650': '{\\sagittarius}', + u'\u2651': '{\\capricornus}', + u'\u2652': '{\\aquarius}', + u'\u2653': '{\\pisces}', + u'\u2660': '{\\ding{171}}', + u'\u2662': '$\\diamond$', + u'\u2663': '{\\ding{168}}', + u'\u2665': '{\\ding{170}}', + u'\u2666': '{\\ding{169}}', + u'\u2669': '{\\quarternote}', + u'\u266a': '{\\eighthnote}', + u'\u266d': '$\\flat$', + u'\u266e': '$\\natural$', + u'\u266f': '$\\sharp$', + u'\u2701': '{\\ding{33}}', + u'\u2702': '{\\ding{34}}', + u'\u2703': '{\\ding{35}}', + u'\u2704': '{\\ding{36}}', + u'\u2706': '{\\ding{38}}', + u'\u2707': '{\\ding{39}}', + u'\u2708': '{\\ding{40}}', + u'\u2709': '{\\ding{41}}', + u'\u270c': '{\\ding{44}}', + u'\u270d': '{\\ding{45}}', + u'\u270e': '{\\ding{46}}', + u'\u270f': '{\\ding{47}}', + u'\u2710': '{\\ding{48}}', + u'\u2711': '{\\ding{49}}', + u'\u2712': '{\\ding{50}}', + u'\u2713': '{\\ding{51}}', + u'\u2714': '{\\ding{52}}', + u'\u2715': '{\\ding{53}}', + u'\u2716': '{\\ding{54}}', + u'\u2717': '{\\ding{55}}', + u'\u2718': '{\\ding{56}}', + u'\u2719': '{\\ding{57}}', + u'\u271a': '{\\ding{58}}', + u'\u271b': '{\\ding{59}}', + u'\u271c': '{\\ding{60}}', + u'\u271d': '{\\ding{61}}', + u'\u271e': '{\\ding{62}}', + u'\u271f': '{\\ding{63}}', + u'\u2720': '{\\ding{64}}', + u'\u2721': '{\\ding{65}}', + u'\u2722': '{\\ding{66}}', + u'\u2723': '{\\ding{67}}', + u'\u2724': '{\\ding{68}}', + u'\u2725': '{\\ding{69}}', + u'\u2726': '{\\ding{70}}', + u'\u2727': '{\\ding{71}}', + u'\u2729': '{\\ding{73}}', + u'\u272a': '{\\ding{74}}', + u'\u272b': '{\\ding{75}}', + u'\u272c': '{\\ding{76}}', + u'\u272d': '{\\ding{77}}', + u'\u272e': '{\\ding{78}}', + u'\u272f': '{\\ding{79}}', + u'\u2730': '{\\ding{80}}', + u'\u2731': '{\\ding{81}}', + u'\u2732': '{\\ding{82}}', + u'\u2733': '{\\ding{83}}', + u'\u2734': '{\\ding{84}}', + u'\u2735': '{\\ding{85}}', + u'\u2736': '{\\ding{86}}', + u'\u2737': '{\\ding{87}}', + u'\u2738': '{\\ding{88}}', + u'\u2739': '{\\ding{89}}', + u'\u273a': '{\\ding{90}}', + u'\u273b': '{\\ding{91}}', + u'\u273c': '{\\ding{92}}', + u'\u273d': '{\\ding{93}}', + u'\u273e': '{\\ding{94}}', + u'\u273f': '{\\ding{95}}', + u'\u2740': '{\\ding{96}}', + u'\u2741': '{\\ding{97}}', + u'\u2742': '{\\ding{98}}', + u'\u2743': '{\\ding{99}}', + u'\u2744': '{\\ding{100}}', + u'\u2745': '{\\ding{101}}', + u'\u2746': '{\\ding{102}}', + u'\u2747': '{\\ding{103}}', + u'\u2748': '{\\ding{104}}', + u'\u2749': '{\\ding{105}}', + u'\u274a': '{\\ding{106}}', + u'\u274b': '{\\ding{107}}', + u'\u274d': '{\\ding{109}}', + u'\u274f': '{\\ding{111}}', + u'\u2750': '{\\ding{112}}', + u'\u2751': '{\\ding{113}}', + u'\u2752': '{\\ding{114}}', + u'\u2756': '{\\ding{118}}', + u'\u2758': '{\\ding{120}}', + u'\u2759': '{\\ding{121}}', + u'\u275a': '{\\ding{122}}', + u'\u275b': '{\\ding{123}}', + u'\u275c': '{\\ding{124}}', + u'\u275d': '{\\ding{125}}', + u'\u275e': '{\\ding{126}}', + u'\u2761': '{\\ding{161}}', + u'\u2762': '{\\ding{162}}', + u'\u2763': '{\\ding{163}}', + u'\u2764': '{\\ding{164}}', + u'\u2765': '{\\ding{165}}', + u'\u2766': '{\\ding{166}}', + u'\u2767': '{\\ding{167}}', + u'\u2776': '{\\ding{182}}', + u'\u2777': '{\\ding{183}}', + u'\u2778': '{\\ding{184}}', + u'\u2779': '{\\ding{185}}', + u'\u277a': '{\\ding{186}}', + u'\u277b': '{\\ding{187}}', + u'\u277c': '{\\ding{188}}', + u'\u277d': '{\\ding{189}}', + u'\u277e': '{\\ding{190}}', + u'\u277f': '{\\ding{191}}', + u'\u2780': '{\\ding{192}}', + u'\u2781': '{\\ding{193}}', + u'\u2782': '{\\ding{194}}', + u'\u2783': '{\\ding{195}}', + u'\u2784': '{\\ding{196}}', + u'\u2785': '{\\ding{197}}', + u'\u2786': '{\\ding{198}}', + u'\u2787': '{\\ding{199}}', + u'\u2788': '{\\ding{200}}', + u'\u2789': '{\\ding{201}}', + u'\u278a': '{\\ding{202}}', + u'\u278b': '{\\ding{203}}', + u'\u278c': '{\\ding{204}}', + u'\u278d': '{\\ding{205}}', + u'\u278e': '{\\ding{206}}', + u'\u278f': '{\\ding{207}}', + u'\u2790': '{\\ding{208}}', + u'\u2791': '{\\ding{209}}', + u'\u2792': '{\\ding{210}}', + u'\u2793': '{\\ding{211}}', + u'\u2794': '{\\ding{212}}', + u'\u2798': '{\\ding{216}}', + u'\u2799': '{\\ding{217}}', + u'\u279a': '{\\ding{218}}', + u'\u279b': '{\\ding{219}}', + u'\u279c': '{\\ding{220}}', + u'\u279d': '{\\ding{221}}', + u'\u279e': '{\\ding{222}}', + u'\u279f': '{\\ding{223}}', + u'\u27a0': '{\\ding{224}}', + u'\u27a1': '{\\ding{225}}', + u'\u27a2': '{\\ding{226}}', + u'\u27a3': '{\\ding{227}}', + u'\u27a4': '{\\ding{228}}', + u'\u27a5': '{\\ding{229}}', + u'\u27a6': '{\\ding{230}}', + u'\u27a7': '{\\ding{231}}', + u'\u27a8': '{\\ding{232}}', + u'\u27a9': '{\\ding{233}}', + u'\u27aa': '{\\ding{234}}', + u'\u27ab': '{\\ding{235}}', + u'\u27ac': '{\\ding{236}}', + u'\u27ad': '{\\ding{237}}', + u'\u27ae': '{\\ding{238}}', + u'\u27af': '{\\ding{239}}', + u'\u27b1': '{\\ding{241}}', + u'\u27b2': '{\\ding{242}}', + u'\u27b3': '{\\ding{243}}', + u'\u27b4': '{\\ding{244}}', + u'\u27b5': '{\\ding{245}}', + u'\u27b6': '{\\ding{246}}', + u'\u27b7': '{\\ding{247}}', + u'\u27b8': '{\\ding{248}}', + u'\u27b9': '{\\ding{249}}', + u'\u27ba': '{\\ding{250}}', + u'\u27bb': '{\\ding{251}}', + u'\u27bc': '{\\ding{252}}', + u'\u27bd': '{\\ding{253}}', + u'\u27be': '{\\ding{254}}', + u'\u27f5': '$\\longleftarrow$', + u'\u27f6': '$\\longrightarrow$', + u'\u27f7': '$\\longleftrightarrow$', + u'\u27f8': '$\\Longleftarrow$', + u'\u27f9': '$\\Longrightarrow$', + u'\u27fa': '$\\Longleftrightarrow$', + u'\u27fc': '$\\longmapsto$', + u'\u27ff': '$\\sim\\joinrel\\leadsto$', + u'\u2905': '$\\ElsevierGlyph{E212}$', + u'\u2912': '$\\UpArrowBar$', + u'\u2913': '$\\DownArrowBar$', + u'\u2923': '$\\ElsevierGlyph{E20C}$', + u'\u2924': '$\\ElsevierGlyph{E20D}$', + u'\u2925': '$\\ElsevierGlyph{E20B}$', + u'\u2926': '$\\ElsevierGlyph{E20A}$', + u'\u2927': '$\\ElsevierGlyph{E211}$', + u'\u2928': '$\\ElsevierGlyph{E20E}$', + u'\u2929': '$\\ElsevierGlyph{E20F}$', + u'\u292a': '$\\ElsevierGlyph{E210}$', + u'\u2933': '$\\ElsevierGlyph{E21C}$', + u'\u2936': '$\\ElsevierGlyph{E21A}$', + u'\u2937': '$\\ElsevierGlyph{E219}$', + u'\u2940': '$\\Elolarr$', + u'\u2941': '$\\Elorarr$', + u'\u2942': '$\\ElzRlarr$', + u'\u2944': '$\\ElzrLarr$', + u'\u2947': '$\\Elzrarrx$', + u'\u294e': '$\\LeftRightVector$', + u'\u294f': '$\\RightUpDownVector$', + u'\u2950': '$\\DownLeftRightVector$', + u'\u2951': '$\\LeftUpDownVector$', + u'\u2952': '$\\LeftVectorBar$', + u'\u2953': '$\\RightVectorBar$', + u'\u2954': '$\\RightUpVectorBar$', + u'\u2955': '$\\RightDownVectorBar$', + u'\u2956': '$\\DownLeftVectorBar$', + u'\u2957': '$\\DownRightVectorBar$', + u'\u2958': '$\\LeftUpVectorBar$', + u'\u2959': '$\\LeftDownVectorBar$', + u'\u295a': '$\\LeftTeeVector$', + u'\u295b': '$\\RightTeeVector$', + u'\u295c': '$\\RightUpTeeVector$', + u'\u295d': '$\\RightDownTeeVector$', + u'\u295e': '$\\DownLeftTeeVector$', + u'\u295f': '$\\DownRightTeeVector$', + u'\u2960': '$\\LeftUpTeeVector$', + u'\u2961': '$\\LeftDownTeeVector$', + u'\u296e': '$\\UpEquilibrium$', + u'\u296f': '$\\ReverseUpEquilibrium$', + u'\u2970': '$\\RoundImplies$', + u'\u297c': '$\\ElsevierGlyph{E214}$', + u'\u297d': '$\\ElsevierGlyph{E215}$', + u'\u2980': '$\\Elztfnc$', + u'\u2985': '$\\ElsevierGlyph{3018}$', + u'\u2986': '$\\Elroang$', + u'\u2993': '$<\\kern-0.58em($', + u'\u2994': '$\\ElsevierGlyph{E291}$', + u'\u2999': '$\\Elzddfnc$', + u'\u299c': '$\\Angle$', + u'\u29a0': '$\\Elzlpargt$', + u'\u29b5': '$\\ElsevierGlyph{E260}$', + u'\u29b6': '$\\ElsevierGlyph{E61B}$', + u'\u29ca': '$\\ElzLap$', + u'\u29cb': '$\\Elzdefas$', + u'\u29cf': '$\\LeftTriangleBar$', + u'\u29d0': '$\\RightTriangleBar$', + u'\u29dc': '$\\ElsevierGlyph{E372}$', + u'\u29eb': '$\\blacklozenge$', + u'\u29f4': '$\\RuleDelayed$', + u'\u2a04': '$\\Elxuplus$', + u'\u2a05': '$\\ElzThr$', + u'\u2a06': '$\\Elxsqcup$', + u'\u2a07': '$\\ElzInf$', + u'\u2a08': '$\\ElzSup$', + u'\u2a0d': '$\\ElzCint$', + u'\u2a0f': '$\\clockoint$', + u'\u2a10': '$\\ElsevierGlyph{E395}$', + u'\u2a16': '$\\sqrint$', + u'\u2a25': '$\\ElsevierGlyph{E25A}$', + u'\u2a2a': '$\\ElsevierGlyph{E25B}$', + u'\u2a2d': '$\\ElsevierGlyph{E25C}$', + u'\u2a2e': '$\\ElsevierGlyph{E25D}$', + u'\u2a2f': '$\\ElzTimes$', + u'\u2a34': '$\\ElsevierGlyph{E25E}$', + u'\u2a35': '$\\ElsevierGlyph{E25E}$', + u'\u2a3c': '$\\ElsevierGlyph{E259}$', + u'\u2a3f': '$\\amalg$', + u'\u2a53': '$\\ElzAnd$', + u'\u2a54': '$\\ElzOr$', + u'\u2a55': '$\\ElsevierGlyph{E36E}$', + u'\u2a56': '$\\ElOr$', + u'\u2a5e': '$\\perspcorrespond$', + u'\u2a5f': '$\\Elzminhat$', + u'\u2a63': '$\\ElsevierGlyph{225A}$', + u'\u2a6e': '$\\stackrel{*}{=}$', + u'\u2a75': '$\\Equal$', + u'\u2a7d': '$\\leqslant$', + u'\u2a7e': '$\\geqslant$', + u'\u2a85': '$\\lessapprox$', + u'\u2a86': '$\\gtrapprox$', + u'\u2a87': '$\\lneq$', + u'\u2a88': '$\\gneq$', + u'\u2a89': '$\\lnapprox$', + u'\u2a8a': '$\\gnapprox$', + u'\u2a8b': '$\\lesseqqgtr$', + u'\u2a8c': '$\\gtreqqless$', + u'\u2a95': '$\\eqslantless$', + u'\u2a96': '$\\eqslantgtr$', + u'\u2a9d': '$\\Pisymbol{ppi020}{117}$', + u'\u2a9e': '$\\Pisymbol{ppi020}{105}$', + u'\u2aa1': '$\\NestedLessLess$', + u'\u2aa2': '$\\NestedGreaterGreater$', + u'\u2aaf': '$\\preceq$', + u'\u2ab0': '$\\succeq$', + u'\u2ab5': '$\\precneqq$', + u'\u2ab6': '$\\succneqq$', + u'\u2ab7': '$\\precapprox$', + u'\u2ab8': '$\\succapprox$', + u'\u2ab9': '$\\precnapprox$', + u'\u2aba': '$\\succnapprox$', + u'\u2ac5': '$\\subseteqq$', + u'\u2ac6': '$\\supseteqq$', + u'\u2acb': '$\\subsetneqq$', + u'\u2acc': '$\\supsetneqq$', + u'\u2aeb': '$\\ElsevierGlyph{E30D}$', + u'\u2af6': '$\\Elztdcol$', + u'\u2afd': '${{/}\\!\\!{/}}$', + u'\u300a': '$\\ElsevierGlyph{300A}$', + u'\u300b': '$\\ElsevierGlyph{300B}$', + u'\u3018': '$\\ElsevierGlyph{3018}$', + u'\u3019': '$\\ElsevierGlyph{3019}$', + u'\u301a': '$\\openbracketleft$', + u'\u301b': '$\\openbracketright$', + u'\ufb00': '{ff}', + u'\ufb01': '{fi}', + u'\ufb02': '{fl}', + u'\ufb03': '{ffi}', + u'\ufb04': '{ffl}', + u'\U0001d400': '$\\mathbf{A}$', + u'\U0001d401': '$\\mathbf{B}$', + u'\U0001d402': '$\\mathbf{C}$', + u'\U0001d403': '$\\mathbf{D}$', + u'\U0001d404': '$\\mathbf{E}$', + u'\U0001d405': '$\\mathbf{F}$', + u'\U0001d406': '$\\mathbf{G}$', + u'\U0001d407': '$\\mathbf{H}$', + u'\U0001d408': '$\\mathbf{I}$', + u'\U0001d409': '$\\mathbf{J}$', + u'\U0001d40a': '$\\mathbf{K}$', + u'\U0001d40b': '$\\mathbf{L}$', + u'\U0001d40c': '$\\mathbf{M}$', + u'\U0001d40d': '$\\mathbf{N}$', + u'\U0001d40e': '$\\mathbf{O}$', + u'\U0001d40f': '$\\mathbf{P}$', + u'\U0001d410': '$\\mathbf{Q}$', + u'\U0001d411': '$\\mathbf{R}$', + u'\U0001d412': '$\\mathbf{S}$', + u'\U0001d413': '$\\mathbf{T}$', + u'\U0001d414': '$\\mathbf{U}$', + u'\U0001d415': '$\\mathbf{V}$', + u'\U0001d416': '$\\mathbf{W}$', + u'\U0001d417': '$\\mathbf{X}$', + u'\U0001d418': '$\\mathbf{Y}$', + u'\U0001d419': '$\\mathbf{Z}$', + u'\U0001d41a': '$\\mathbf{a}$', + u'\U0001d41b': '$\\mathbf{b}$', + u'\U0001d41c': '$\\mathbf{c}$', + u'\U0001d41d': '$\\mathbf{d}$', + u'\U0001d41e': '$\\mathbf{e}$', + u'\U0001d41f': '$\\mathbf{f}$', + u'\U0001d420': '$\\mathbf{g}$', + u'\U0001d421': '$\\mathbf{h}$', + u'\U0001d422': '$\\mathbf{i}$', + u'\U0001d423': '$\\mathbf{j}$', + u'\U0001d424': '$\\mathbf{k}$', + u'\U0001d425': '$\\mathbf{l}$', + u'\U0001d426': '$\\mathbf{m}$', + u'\U0001d427': '$\\mathbf{n}$', + u'\U0001d428': '$\\mathbf{o}$', + u'\U0001d429': '$\\mathbf{p}$', + u'\U0001d42a': '$\\mathbf{q}$', + u'\U0001d42b': '$\\mathbf{r}$', + u'\U0001d42c': '$\\mathbf{s}$', + u'\U0001d42d': '$\\mathbf{t}$', + u'\U0001d42e': '$\\mathbf{u}$', + u'\U0001d42f': '$\\mathbf{v}$', + u'\U0001d430': '$\\mathbf{w}$', + u'\U0001d431': '$\\mathbf{x}$', + u'\U0001d432': '$\\mathbf{y}$', + u'\U0001d433': '$\\mathbf{z}$', + u'\U0001d434': '$\\mathsl{A}$', + u'\U0001d435': '$\\mathsl{B}$', + u'\U0001d436': '$\\mathsl{C}$', + u'\U0001d437': '$\\mathsl{D}$', + u'\U0001d438': '$\\mathsl{E}$', + u'\U0001d439': '$\\mathsl{F}$', + u'\U0001d43a': '$\\mathsl{G}$', + u'\U0001d43b': '$\\mathsl{H}$', + u'\U0001d43c': '$\\mathsl{I}$', + u'\U0001d43d': '$\\mathsl{J}$', + u'\U0001d43e': '$\\mathsl{K}$', + u'\U0001d43f': '$\\mathsl{L}$', + u'\U0001d440': '$\\mathsl{M}$', + u'\U0001d441': '$\\mathsl{N}$', + u'\U0001d442': '$\\mathsl{O}$', + u'\U0001d443': '$\\mathsl{P}$', + u'\U0001d444': '$\\mathsl{Q}$', + u'\U0001d445': '$\\mathsl{R}$', + u'\U0001d446': '$\\mathsl{S}$', + u'\U0001d447': '$\\mathsl{T}$', + u'\U0001d448': '$\\mathsl{U}$', + u'\U0001d449': '$\\mathsl{V}$', + u'\U0001d44a': '$\\mathsl{W}$', + u'\U0001d44b': '$\\mathsl{X}$', + u'\U0001d44c': '$\\mathsl{Y}$', + u'\U0001d44d': '$\\mathsl{Z}$', + u'\U0001d44e': '$\\mathsl{a}$', + u'\U0001d44f': '$\\mathsl{b}$', + u'\U0001d450': '$\\mathsl{c}$', + u'\U0001d451': '$\\mathsl{d}$', + u'\U0001d452': '$\\mathsl{e}$', + u'\U0001d453': '$\\mathsl{f}$', + u'\U0001d454': '$\\mathsl{g}$', + u'\U0001d456': '$\\mathsl{i}$', + u'\U0001d457': '$\\mathsl{j}$', + u'\U0001d458': '$\\mathsl{k}$', + u'\U0001d459': '$\\mathsl{l}$', + u'\U0001d45a': '$\\mathsl{m}$', + u'\U0001d45b': '$\\mathsl{n}$', + u'\U0001d45c': '$\\mathsl{o}$', + u'\U0001d45d': '$\\mathsl{p}$', + u'\U0001d45e': '$\\mathsl{q}$', + u'\U0001d45f': '$\\mathsl{r}$', + u'\U0001d460': '$\\mathsl{s}$', + u'\U0001d461': '$\\mathsl{t}$', + u'\U0001d462': '$\\mathsl{u}$', + u'\U0001d463': '$\\mathsl{v}$', + u'\U0001d464': '$\\mathsl{w}$', + u'\U0001d465': '$\\mathsl{x}$', + u'\U0001d466': '$\\mathsl{y}$', + u'\U0001d467': '$\\mathsl{z}$', + u'\U0001d468': '$\\mathbit{A}$', + u'\U0001d469': '$\\mathbit{B}$', + u'\U0001d46a': '$\\mathbit{C}$', + u'\U0001d46b': '$\\mathbit{D}$', + u'\U0001d46c': '$\\mathbit{E}$', + u'\U0001d46d': '$\\mathbit{F}$', + u'\U0001d46e': '$\\mathbit{G}$', + u'\U0001d46f': '$\\mathbit{H}$', + u'\U0001d470': '$\\mathbit{I}$', + u'\U0001d471': '$\\mathbit{J}$', + u'\U0001d472': '$\\mathbit{K}$', + u'\U0001d473': '$\\mathbit{L}$', + u'\U0001d474': '$\\mathbit{M}$', + u'\U0001d475': '$\\mathbit{N}$', + u'\U0001d476': '$\\mathbit{O}$', + u'\U0001d477': '$\\mathbit{P}$', + u'\U0001d478': '$\\mathbit{Q}$', + u'\U0001d479': '$\\mathbit{R}$', + u'\U0001d47a': '$\\mathbit{S}$', + u'\U0001d47b': '$\\mathbit{T}$', + u'\U0001d47c': '$\\mathbit{U}$', + u'\U0001d47d': '$\\mathbit{V}$', + u'\U0001d47e': '$\\mathbit{W}$', + u'\U0001d47f': '$\\mathbit{X}$', + u'\U0001d480': '$\\mathbit{Y}$', + u'\U0001d481': '$\\mathbit{Z}$', + u'\U0001d482': '$\\mathbit{a}$', + u'\U0001d483': '$\\mathbit{b}$', + u'\U0001d484': '$\\mathbit{c}$', + u'\U0001d485': '$\\mathbit{d}$', + u'\U0001d486': '$\\mathbit{e}$', + u'\U0001d487': '$\\mathbit{f}$', + u'\U0001d488': '$\\mathbit{g}$', + u'\U0001d489': '$\\mathbit{h}$', + u'\U0001d48a': '$\\mathbit{i}$', + u'\U0001d48b': '$\\mathbit{j}$', + u'\U0001d48c': '$\\mathbit{k}$', + u'\U0001d48d': '$\\mathbit{l}$', + u'\U0001d48e': '$\\mathbit{m}$', + u'\U0001d48f': '$\\mathbit{n}$', + u'\U0001d490': '$\\mathbit{o}$', + u'\U0001d491': '$\\mathbit{p}$', + u'\U0001d492': '$\\mathbit{q}$', + u'\U0001d493': '$\\mathbit{r}$', + u'\U0001d494': '$\\mathbit{s}$', + u'\U0001d495': '$\\mathbit{t}$', + u'\U0001d496': '$\\mathbit{u}$', + u'\U0001d497': '$\\mathbit{v}$', + u'\U0001d498': '$\\mathbit{w}$', + u'\U0001d499': '$\\mathbit{x}$', + u'\U0001d49a': '$\\mathbit{y}$', + u'\U0001d49b': '$\\mathbit{z}$', + u'\U0001d49c': '$\\mathscr{A}$', + u'\U0001d49e': '$\\mathscr{C}$', + u'\U0001d49f': '$\\mathscr{D}$', + u'\U0001d4a2': '$\\mathscr{G}$', + u'\U0001d4a5': '$\\mathscr{J}$', + u'\U0001d4a6': '$\\mathscr{K}$', + u'\U0001d4a9': '$\\mathscr{N}$', + u'\U0001d4aa': '$\\mathscr{O}$', + u'\U0001d4ab': '$\\mathscr{P}$', + u'\U0001d4ac': '$\\mathscr{Q}$', + u'\U0001d4ae': '$\\mathscr{S}$', + u'\U0001d4af': '$\\mathscr{T}$', + u'\U0001d4b0': '$\\mathscr{U}$', + u'\U0001d4b1': '$\\mathscr{V}$', + u'\U0001d4b2': '$\\mathscr{W}$', + u'\U0001d4b3': '$\\mathscr{X}$', + u'\U0001d4b4': '$\\mathscr{Y}$', + u'\U0001d4b5': '$\\mathscr{Z}$', + u'\U0001d4b6': '$\\mathscr{a}$', + u'\U0001d4b7': '$\\mathscr{b}$', + u'\U0001d4b8': '$\\mathscr{c}$', + u'\U0001d4b9': '$\\mathscr{d}$', + u'\U0001d4bb': '$\\mathscr{f}$', + u'\U0001d4bd': '$\\mathscr{h}$', + u'\U0001d4be': '$\\mathscr{i}$', + u'\U0001d4bf': '$\\mathscr{j}$', + u'\U0001d4c0': '$\\mathscr{k}$', + u'\U0001d4c1': '$\\mathscr{l}$', + u'\U0001d4c2': '$\\mathscr{m}$', + u'\U0001d4c3': '$\\mathscr{n}$', + u'\U0001d4c5': '$\\mathscr{p}$', + u'\U0001d4c6': '$\\mathscr{q}$', + u'\U0001d4c7': '$\\mathscr{r}$', + u'\U0001d4c8': '$\\mathscr{s}$', + u'\U0001d4c9': '$\\mathscr{t}$', + u'\U0001d4ca': '$\\mathscr{u}$', + u'\U0001d4cb': '$\\mathscr{v}$', + u'\U0001d4cc': '$\\mathscr{w}$', + u'\U0001d4cd': '$\\mathscr{x}$', + u'\U0001d4ce': '$\\mathscr{y}$', + u'\U0001d4cf': '$\\mathscr{z}$', + u'\U0001d4d0': '$\\mathmit{A}$', + u'\U0001d4d1': '$\\mathmit{B}$', + u'\U0001d4d2': '$\\mathmit{C}$', + u'\U0001d4d3': '$\\mathmit{D}$', + u'\U0001d4d4': '$\\mathmit{E}$', + u'\U0001d4d5': '$\\mathmit{F}$', + u'\U0001d4d6': '$\\mathmit{G}$', + u'\U0001d4d7': '$\\mathmit{H}$', + u'\U0001d4d8': '$\\mathmit{I}$', + u'\U0001d4d9': '$\\mathmit{J}$', + u'\U0001d4da': '$\\mathmit{K}$', + u'\U0001d4db': '$\\mathmit{L}$', + u'\U0001d4dc': '$\\mathmit{M}$', + u'\U0001d4dd': '$\\mathmit{N}$', + u'\U0001d4de': '$\\mathmit{O}$', + u'\U0001d4df': '$\\mathmit{P}$', + u'\U0001d4e0': '$\\mathmit{Q}$', + u'\U0001d4e1': '$\\mathmit{R}$', + u'\U0001d4e2': '$\\mathmit{S}$', + u'\U0001d4e3': '$\\mathmit{T}$', + u'\U0001d4e4': '$\\mathmit{U}$', + u'\U0001d4e5': '$\\mathmit{V}$', + u'\U0001d4e6': '$\\mathmit{W}$', + u'\U0001d4e7': '$\\mathmit{X}$', + u'\U0001d4e8': '$\\mathmit{Y}$', + u'\U0001d4e9': '$\\mathmit{Z}$', + u'\U0001d4ea': '$\\mathmit{a}$', + u'\U0001d4eb': '$\\mathmit{b}$', + u'\U0001d4ec': '$\\mathmit{c}$', + u'\U0001d4ed': '$\\mathmit{d}$', + u'\U0001d4ee': '$\\mathmit{e}$', + u'\U0001d4ef': '$\\mathmit{f}$', + u'\U0001d4f0': '$\\mathmit{g}$', + u'\U0001d4f1': '$\\mathmit{h}$', + u'\U0001d4f2': '$\\mathmit{i}$', + u'\U0001d4f3': '$\\mathmit{j}$', + u'\U0001d4f4': '$\\mathmit{k}$', + u'\U0001d4f5': '$\\mathmit{l}$', + u'\U0001d4f6': '$\\mathmit{m}$', + u'\U0001d4f7': '$\\mathmit{n}$', + u'\U0001d4f8': '$\\mathmit{o}$', + u'\U0001d4f9': '$\\mathmit{p}$', + u'\U0001d4fa': '$\\mathmit{q}$', + u'\U0001d4fb': '$\\mathmit{r}$', + u'\U0001d4fc': '$\\mathmit{s}$', + u'\U0001d4fd': '$\\mathmit{t}$', + u'\U0001d4fe': '$\\mathmit{u}$', + u'\U0001d4ff': '$\\mathmit{v}$', + u'\U0001d500': '$\\mathmit{w}$', + u'\U0001d501': '$\\mathmit{x}$', + u'\U0001d502': '$\\mathmit{y}$', + u'\U0001d503': '$\\mathmit{z}$', + u'\U0001d504': '$\\mathfrak{A}$', + u'\U0001d505': '$\\mathfrak{B}$', + u'\U0001d507': '$\\mathfrak{D}$', + u'\U0001d508': '$\\mathfrak{E}$', + u'\U0001d509': '$\\mathfrak{F}$', + u'\U0001d50a': '$\\mathfrak{G}$', + u'\U0001d50d': '$\\mathfrak{J}$', + u'\U0001d50e': '$\\mathfrak{K}$', + u'\U0001d50f': '$\\mathfrak{L}$', + u'\U0001d510': '$\\mathfrak{M}$', + u'\U0001d511': '$\\mathfrak{N}$', + u'\U0001d512': '$\\mathfrak{O}$', + u'\U0001d513': '$\\mathfrak{P}$', + u'\U0001d514': '$\\mathfrak{Q}$', + u'\U0001d516': '$\\mathfrak{S}$', + u'\U0001d517': '$\\mathfrak{T}$', + u'\U0001d518': '$\\mathfrak{U}$', + u'\U0001d519': '$\\mathfrak{V}$', + u'\U0001d51a': '$\\mathfrak{W}$', + u'\U0001d51b': '$\\mathfrak{X}$', + u'\U0001d51c': '$\\mathfrak{Y}$', + u'\U0001d51e': '$\\mathfrak{a}$', + u'\U0001d51f': '$\\mathfrak{b}$', + u'\U0001d520': '$\\mathfrak{c}$', + u'\U0001d521': '$\\mathfrak{d}$', + u'\U0001d522': '$\\mathfrak{e}$', + u'\U0001d523': '$\\mathfrak{f}$', + u'\U0001d524': '$\\mathfrak{g}$', + u'\U0001d525': '$\\mathfrak{h}$', + u'\U0001d526': '$\\mathfrak{i}$', + u'\U0001d527': '$\\mathfrak{j}$', + u'\U0001d528': '$\\mathfrak{k}$', + u'\U0001d529': '$\\mathfrak{l}$', + u'\U0001d52a': '$\\mathfrak{m}$', + u'\U0001d52b': '$\\mathfrak{n}$', + u'\U0001d52c': '$\\mathfrak{o}$', + u'\U0001d52d': '$\\mathfrak{p}$', + u'\U0001d52e': '$\\mathfrak{q}$', + u'\U0001d52f': '$\\mathfrak{r}$', + u'\U0001d530': '$\\mathfrak{s}$', + u'\U0001d531': '$\\mathfrak{t}$', + u'\U0001d532': '$\\mathfrak{u}$', + u'\U0001d533': '$\\mathfrak{v}$', + u'\U0001d534': '$\\mathfrak{w}$', + u'\U0001d535': '$\\mathfrak{x}$', + u'\U0001d536': '$\\mathfrak{y}$', + u'\U0001d537': '$\\mathfrak{z}$', + u'\U0001d538': '$\\mathbb{A}$', + u'\U0001d539': '$\\mathbb{B}$', + u'\U0001d53b': '$\\mathbb{D}$', + u'\U0001d53c': '$\\mathbb{E}$', + u'\U0001d53d': '$\\mathbb{F}$', + u'\U0001d53e': '$\\mathbb{G}$', + u'\U0001d540': '$\\mathbb{I}$', + u'\U0001d541': '$\\mathbb{J}$', + u'\U0001d542': '$\\mathbb{K}$', + u'\U0001d543': '$\\mathbb{L}$', + u'\U0001d544': '$\\mathbb{M}$', + u'\U0001d546': '$\\mathbb{O}$', + u'\U0001d54a': '$\\mathbb{S}$', + u'\U0001d54b': '$\\mathbb{T}$', + u'\U0001d54c': '$\\mathbb{U}$', + u'\U0001d54d': '$\\mathbb{V}$', + u'\U0001d54e': '$\\mathbb{W}$', + u'\U0001d54f': '$\\mathbb{X}$', + u'\U0001d550': '$\\mathbb{Y}$', + u'\U0001d552': '$\\mathbb{a}$', + u'\U0001d553': '$\\mathbb{b}$', + u'\U0001d554': '$\\mathbb{c}$', + u'\U0001d555': '$\\mathbb{d}$', + u'\U0001d556': '$\\mathbb{e}$', + u'\U0001d557': '$\\mathbb{f}$', + u'\U0001d558': '$\\mathbb{g}$', + u'\U0001d559': '$\\mathbb{h}$', + u'\U0001d55a': '$\\mathbb{i}$', + u'\U0001d55b': '$\\mathbb{j}$', + u'\U0001d55c': '$\\mathbb{k}$', + u'\U0001d55d': '$\\mathbb{l}$', + u'\U0001d55e': '$\\mathbb{m}$', + u'\U0001d55f': '$\\mathbb{n}$', + u'\U0001d560': '$\\mathbb{o}$', + u'\U0001d561': '$\\mathbb{p}$', + u'\U0001d562': '$\\mathbb{q}$', + u'\U0001d563': '$\\mathbb{r}$', + u'\U0001d564': '$\\mathbb{s}$', + u'\U0001d565': '$\\mathbb{t}$', + u'\U0001d566': '$\\mathbb{u}$', + u'\U0001d567': '$\\mathbb{v}$', + u'\U0001d568': '$\\mathbb{w}$', + u'\U0001d569': '$\\mathbb{x}$', + u'\U0001d56a': '$\\mathbb{y}$', + u'\U0001d56b': '$\\mathbb{z}$', + u'\U0001d56c': '$\\mathslbb{A}$', + u'\U0001d56d': '$\\mathslbb{B}$', + u'\U0001d56e': '$\\mathslbb{C}$', + u'\U0001d56f': '$\\mathslbb{D}$', + u'\U0001d570': '$\\mathslbb{E}$', + u'\U0001d571': '$\\mathslbb{F}$', + u'\U0001d572': '$\\mathslbb{G}$', + u'\U0001d573': '$\\mathslbb{H}$', + u'\U0001d574': '$\\mathslbb{I}$', + u'\U0001d575': '$\\mathslbb{J}$', + u'\U0001d576': '$\\mathslbb{K}$', + u'\U0001d577': '$\\mathslbb{L}$', + u'\U0001d578': '$\\mathslbb{M}$', + u'\U0001d579': '$\\mathslbb{N}$', + u'\U0001d57a': '$\\mathslbb{O}$', + u'\U0001d57b': '$\\mathslbb{P}$', + u'\U0001d57c': '$\\mathslbb{Q}$', + u'\U0001d57d': '$\\mathslbb{R}$', + u'\U0001d57e': '$\\mathslbb{S}$', + u'\U0001d57f': '$\\mathslbb{T}$', + u'\U0001d580': '$\\mathslbb{U}$', + u'\U0001d581': '$\\mathslbb{V}$', + u'\U0001d582': '$\\mathslbb{W}$', + u'\U0001d583': '$\\mathslbb{X}$', + u'\U0001d584': '$\\mathslbb{Y}$', + u'\U0001d585': '$\\mathslbb{Z}$', + u'\U0001d586': '$\\mathslbb{a}$', + u'\U0001d587': '$\\mathslbb{b}$', + u'\U0001d588': '$\\mathslbb{c}$', + u'\U0001d589': '$\\mathslbb{d}$', + u'\U0001d58a': '$\\mathslbb{e}$', + u'\U0001d58b': '$\\mathslbb{f}$', + u'\U0001d58c': '$\\mathslbb{g}$', + u'\U0001d58d': '$\\mathslbb{h}$', + u'\U0001d58e': '$\\mathslbb{i}$', + u'\U0001d58f': '$\\mathslbb{j}$', + u'\U0001d590': '$\\mathslbb{k}$', + u'\U0001d591': '$\\mathslbb{l}$', + u'\U0001d592': '$\\mathslbb{m}$', + u'\U0001d593': '$\\mathslbb{n}$', + u'\U0001d594': '$\\mathslbb{o}$', + u'\U0001d595': '$\\mathslbb{p}$', + u'\U0001d596': '$\\mathslbb{q}$', + u'\U0001d597': '$\\mathslbb{r}$', + u'\U0001d598': '$\\mathslbb{s}$', + u'\U0001d599': '$\\mathslbb{t}$', + u'\U0001d59a': '$\\mathslbb{u}$', + u'\U0001d59b': '$\\mathslbb{v}$', + u'\U0001d59c': '$\\mathslbb{w}$', + u'\U0001d59d': '$\\mathslbb{x}$', + u'\U0001d59e': '$\\mathslbb{y}$', + u'\U0001d59f': '$\\mathslbb{z}$', + u'\U0001d5a0': '$\\mathsf{A}$', + u'\U0001d5a1': '$\\mathsf{B}$', + u'\U0001d5a2': '$\\mathsf{C}$', + u'\U0001d5a3': '$\\mathsf{D}$', + u'\U0001d5a4': '$\\mathsf{E}$', + u'\U0001d5a5': '$\\mathsf{F}$', + u'\U0001d5a6': '$\\mathsf{G}$', + u'\U0001d5a7': '$\\mathsf{H}$', + u'\U0001d5a8': '$\\mathsf{I}$', + u'\U0001d5a9': '$\\mathsf{J}$', + u'\U0001d5aa': '$\\mathsf{K}$', + u'\U0001d5ab': '$\\mathsf{L}$', + u'\U0001d5ac': '$\\mathsf{M}$', + u'\U0001d5ad': '$\\mathsf{N}$', + u'\U0001d5ae': '$\\mathsf{O}$', + u'\U0001d5af': '$\\mathsf{P}$', + u'\U0001d5b0': '$\\mathsf{Q}$', + u'\U0001d5b1': '$\\mathsf{R}$', + u'\U0001d5b2': '$\\mathsf{S}$', + u'\U0001d5b3': '$\\mathsf{T}$', + u'\U0001d5b4': '$\\mathsf{U}$', + u'\U0001d5b5': '$\\mathsf{V}$', + u'\U0001d5b6': '$\\mathsf{W}$', + u'\U0001d5b7': '$\\mathsf{X}$', + u'\U0001d5b8': '$\\mathsf{Y}$', + u'\U0001d5b9': '$\\mathsf{Z}$', + u'\U0001d5ba': '$\\mathsf{a}$', + u'\U0001d5bb': '$\\mathsf{b}$', + u'\U0001d5bc': '$\\mathsf{c}$', + u'\U0001d5bd': '$\\mathsf{d}$', + u'\U0001d5be': '$\\mathsf{e}$', + u'\U0001d5bf': '$\\mathsf{f}$', + u'\U0001d5c0': '$\\mathsf{g}$', + u'\U0001d5c1': '$\\mathsf{h}$', + u'\U0001d5c2': '$\\mathsf{i}$', + u'\U0001d5c3': '$\\mathsf{j}$', + u'\U0001d5c4': '$\\mathsf{k}$', + u'\U0001d5c5': '$\\mathsf{l}$', + u'\U0001d5c6': '$\\mathsf{m}$', + u'\U0001d5c7': '$\\mathsf{n}$', + u'\U0001d5c8': '$\\mathsf{o}$', + u'\U0001d5c9': '$\\mathsf{p}$', + u'\U0001d5ca': '$\\mathsf{q}$', + u'\U0001d5cb': '$\\mathsf{r}$', + u'\U0001d5cc': '$\\mathsf{s}$', + u'\U0001d5cd': '$\\mathsf{t}$', + u'\U0001d5ce': '$\\mathsf{u}$', + u'\U0001d5cf': '$\\mathsf{v}$', + u'\U0001d5d0': '$\\mathsf{w}$', + u'\U0001d5d1': '$\\mathsf{x}$', + u'\U0001d5d2': '$\\mathsf{y}$', + u'\U0001d5d3': '$\\mathsf{z}$', + u'\U0001d5d4': '$\\mathsfbf{A}$', + u'\U0001d5d5': '$\\mathsfbf{B}$', + u'\U0001d5d6': '$\\mathsfbf{C}$', + u'\U0001d5d7': '$\\mathsfbf{D}$', + u'\U0001d5d8': '$\\mathsfbf{E}$', + u'\U0001d5d9': '$\\mathsfbf{F}$', + u'\U0001d5da': '$\\mathsfbf{G}$', + u'\U0001d5db': '$\\mathsfbf{H}$', + u'\U0001d5dc': '$\\mathsfbf{I}$', + u'\U0001d5dd': '$\\mathsfbf{J}$', + u'\U0001d5de': '$\\mathsfbf{K}$', + u'\U0001d5df': '$\\mathsfbf{L}$', + u'\U0001d5e0': '$\\mathsfbf{M}$', + u'\U0001d5e1': '$\\mathsfbf{N}$', + u'\U0001d5e2': '$\\mathsfbf{O}$', + u'\U0001d5e3': '$\\mathsfbf{P}$', + u'\U0001d5e4': '$\\mathsfbf{Q}$', + u'\U0001d5e5': '$\\mathsfbf{R}$', + u'\U0001d5e6': '$\\mathsfbf{S}$', + u'\U0001d5e7': '$\\mathsfbf{T}$', + u'\U0001d5e8': '$\\mathsfbf{U}$', + u'\U0001d5e9': '$\\mathsfbf{V}$', + u'\U0001d5ea': '$\\mathsfbf{W}$', + u'\U0001d5eb': '$\\mathsfbf{X}$', + u'\U0001d5ec': '$\\mathsfbf{Y}$', + u'\U0001d5ed': '$\\mathsfbf{Z}$', + u'\U0001d5ee': '$\\mathsfbf{a}$', + u'\U0001d5ef': '$\\mathsfbf{b}$', + u'\U0001d5f0': '$\\mathsfbf{c}$', + u'\U0001d5f1': '$\\mathsfbf{d}$', + u'\U0001d5f2': '$\\mathsfbf{e}$', + u'\U0001d5f3': '$\\mathsfbf{f}$', + u'\U0001d5f4': '$\\mathsfbf{g}$', + u'\U0001d5f5': '$\\mathsfbf{h}$', + u'\U0001d5f6': '$\\mathsfbf{i}$', + u'\U0001d5f7': '$\\mathsfbf{j}$', + u'\U0001d5f8': '$\\mathsfbf{k}$', + u'\U0001d5f9': '$\\mathsfbf{l}$', + u'\U0001d5fa': '$\\mathsfbf{m}$', + u'\U0001d5fb': '$\\mathsfbf{n}$', + u'\U0001d5fc': '$\\mathsfbf{o}$', + u'\U0001d5fd': '$\\mathsfbf{p}$', + u'\U0001d5fe': '$\\mathsfbf{q}$', + u'\U0001d5ff': '$\\mathsfbf{r}$', + u'\U0001d600': '$\\mathsfbf{s}$', + u'\U0001d601': '$\\mathsfbf{t}$', + u'\U0001d602': '$\\mathsfbf{u}$', + u'\U0001d603': '$\\mathsfbf{v}$', + u'\U0001d604': '$\\mathsfbf{w}$', + u'\U0001d605': '$\\mathsfbf{x}$', + u'\U0001d606': '$\\mathsfbf{y}$', + u'\U0001d607': '$\\mathsfbf{z}$', + u'\U0001d608': '$\\mathsfsl{A}$', + u'\U0001d609': '$\\mathsfsl{B}$', + u'\U0001d60a': '$\\mathsfsl{C}$', + u'\U0001d60b': '$\\mathsfsl{D}$', + u'\U0001d60c': '$\\mathsfsl{E}$', + u'\U0001d60d': '$\\mathsfsl{F}$', + u'\U0001d60e': '$\\mathsfsl{G}$', + u'\U0001d60f': '$\\mathsfsl{H}$', + u'\U0001d610': '$\\mathsfsl{I}$', + u'\U0001d611': '$\\mathsfsl{J}$', + u'\U0001d612': '$\\mathsfsl{K}$', + u'\U0001d613': '$\\mathsfsl{L}$', + u'\U0001d614': '$\\mathsfsl{M}$', + u'\U0001d615': '$\\mathsfsl{N}$', + u'\U0001d616': '$\\mathsfsl{O}$', + u'\U0001d617': '$\\mathsfsl{P}$', + u'\U0001d618': '$\\mathsfsl{Q}$', + u'\U0001d619': '$\\mathsfsl{R}$', + u'\U0001d61a': '$\\mathsfsl{S}$', + u'\U0001d61b': '$\\mathsfsl{T}$', + u'\U0001d61c': '$\\mathsfsl{U}$', + u'\U0001d61d': '$\\mathsfsl{V}$', + u'\U0001d61e': '$\\mathsfsl{W}$', + u'\U0001d61f': '$\\mathsfsl{X}$', + u'\U0001d620': '$\\mathsfsl{Y}$', + u'\U0001d621': '$\\mathsfsl{Z}$', + u'\U0001d622': '$\\mathsfsl{a}$', + u'\U0001d623': '$\\mathsfsl{b}$', + u'\U0001d624': '$\\mathsfsl{c}$', + u'\U0001d625': '$\\mathsfsl{d}$', + u'\U0001d626': '$\\mathsfsl{e}$', + u'\U0001d627': '$\\mathsfsl{f}$', + u'\U0001d628': '$\\mathsfsl{g}$', + u'\U0001d629': '$\\mathsfsl{h}$', + u'\U0001d62a': '$\\mathsfsl{i}$', + u'\U0001d62b': '$\\mathsfsl{j}$', + u'\U0001d62c': '$\\mathsfsl{k}$', + u'\U0001d62d': '$\\mathsfsl{l}$', + u'\U0001d62e': '$\\mathsfsl{m}$', + u'\U0001d62f': '$\\mathsfsl{n}$', + u'\U0001d630': '$\\mathsfsl{o}$', + u'\U0001d631': '$\\mathsfsl{p}$', + u'\U0001d632': '$\\mathsfsl{q}$', + u'\U0001d633': '$\\mathsfsl{r}$', + u'\U0001d634': '$\\mathsfsl{s}$', + u'\U0001d635': '$\\mathsfsl{t}$', + u'\U0001d636': '$\\mathsfsl{u}$', + u'\U0001d637': '$\\mathsfsl{v}$', + u'\U0001d638': '$\\mathsfsl{w}$', + u'\U0001d639': '$\\mathsfsl{x}$', + u'\U0001d63a': '$\\mathsfsl{y}$', + u'\U0001d63b': '$\\mathsfsl{z}$', + u'\U0001d63c': '$\\mathsfbfsl{A}$', + u'\U0001d63d': '$\\mathsfbfsl{B}$', + u'\U0001d63e': '$\\mathsfbfsl{C}$', + u'\U0001d63f': '$\\mathsfbfsl{D}$', + u'\U0001d640': '$\\mathsfbfsl{E}$', + u'\U0001d641': '$\\mathsfbfsl{F}$', + u'\U0001d642': '$\\mathsfbfsl{G}$', + u'\U0001d643': '$\\mathsfbfsl{H}$', + u'\U0001d644': '$\\mathsfbfsl{I}$', + u'\U0001d645': '$\\mathsfbfsl{J}$', + u'\U0001d646': '$\\mathsfbfsl{K}$', + u'\U0001d647': '$\\mathsfbfsl{L}$', + u'\U0001d648': '$\\mathsfbfsl{M}$', + u'\U0001d649': '$\\mathsfbfsl{N}$', + u'\U0001d64a': '$\\mathsfbfsl{O}$', + u'\U0001d64b': '$\\mathsfbfsl{P}$', + u'\U0001d64c': '$\\mathsfbfsl{Q}$', + u'\U0001d64d': '$\\mathsfbfsl{R}$', + u'\U0001d64e': '$\\mathsfbfsl{S}$', + u'\U0001d64f': '$\\mathsfbfsl{T}$', + u'\U0001d650': '$\\mathsfbfsl{U}$', + u'\U0001d651': '$\\mathsfbfsl{V}$', + u'\U0001d652': '$\\mathsfbfsl{W}$', + u'\U0001d653': '$\\mathsfbfsl{X}$', + u'\U0001d654': '$\\mathsfbfsl{Y}$', + u'\U0001d655': '$\\mathsfbfsl{Z}$', + u'\U0001d656': '$\\mathsfbfsl{a}$', + u'\U0001d657': '$\\mathsfbfsl{b}$', + u'\U0001d658': '$\\mathsfbfsl{c}$', + u'\U0001d659': '$\\mathsfbfsl{d}$', + u'\U0001d65a': '$\\mathsfbfsl{e}$', + u'\U0001d65b': '$\\mathsfbfsl{f}$', + u'\U0001d65c': '$\\mathsfbfsl{g}$', + u'\U0001d65d': '$\\mathsfbfsl{h}$', + u'\U0001d65e': '$\\mathsfbfsl{i}$', + u'\U0001d65f': '$\\mathsfbfsl{j}$', + u'\U0001d660': '$\\mathsfbfsl{k}$', + u'\U0001d661': '$\\mathsfbfsl{l}$', + u'\U0001d662': '$\\mathsfbfsl{m}$', + u'\U0001d663': '$\\mathsfbfsl{n}$', + u'\U0001d664': '$\\mathsfbfsl{o}$', + u'\U0001d665': '$\\mathsfbfsl{p}$', + u'\U0001d666': '$\\mathsfbfsl{q}$', + u'\U0001d667': '$\\mathsfbfsl{r}$', + u'\U0001d668': '$\\mathsfbfsl{s}$', + u'\U0001d669': '$\\mathsfbfsl{t}$', + u'\U0001d66a': '$\\mathsfbfsl{u}$', + u'\U0001d66b': '$\\mathsfbfsl{v}$', + u'\U0001d66c': '$\\mathsfbfsl{w}$', + u'\U0001d66d': '$\\mathsfbfsl{x}$', + u'\U0001d66e': '$\\mathsfbfsl{y}$', + u'\U0001d66f': '$\\mathsfbfsl{z}$', + u'\U0001d670': '$\\mathtt{A}$', + u'\U0001d671': '$\\mathtt{B}$', + u'\U0001d672': '$\\mathtt{C}$', + u'\U0001d673': '$\\mathtt{D}$', + u'\U0001d674': '$\\mathtt{E}$', + u'\U0001d675': '$\\mathtt{F}$', + u'\U0001d676': '$\\mathtt{G}$', + u'\U0001d677': '$\\mathtt{H}$', + u'\U0001d678': '$\\mathtt{I}$', + u'\U0001d679': '$\\mathtt{J}$', + u'\U0001d67a': '$\\mathtt{K}$', + u'\U0001d67b': '$\\mathtt{L}$', + u'\U0001d67c': '$\\mathtt{M}$', + u'\U0001d67d': '$\\mathtt{N}$', + u'\U0001d67e': '$\\mathtt{O}$', + u'\U0001d67f': '$\\mathtt{P}$', + u'\U0001d680': '$\\mathtt{Q}$', + u'\U0001d681': '$\\mathtt{R}$', + u'\U0001d682': '$\\mathtt{S}$', + u'\U0001d683': '$\\mathtt{T}$', + u'\U0001d684': '$\\mathtt{U}$', + u'\U0001d685': '$\\mathtt{V}$', + u'\U0001d686': '$\\mathtt{W}$', + u'\U0001d687': '$\\mathtt{X}$', + u'\U0001d688': '$\\mathtt{Y}$', + u'\U0001d689': '$\\mathtt{Z}$', + u'\U0001d68a': '$\\mathtt{a}$', + u'\U0001d68b': '$\\mathtt{b}$', + u'\U0001d68c': '$\\mathtt{c}$', + u'\U0001d68d': '$\\mathtt{d}$', + u'\U0001d68e': '$\\mathtt{e}$', + u'\U0001d68f': '$\\mathtt{f}$', + u'\U0001d690': '$\\mathtt{g}$', + u'\U0001d691': '$\\mathtt{h}$', + u'\U0001d692': '$\\mathtt{i}$', + u'\U0001d693': '$\\mathtt{j}$', + u'\U0001d694': '$\\mathtt{k}$', + u'\U0001d695': '$\\mathtt{l}$', + u'\U0001d696': '$\\mathtt{m}$', + u'\U0001d697': '$\\mathtt{n}$', + u'\U0001d698': '$\\mathtt{o}$', + u'\U0001d699': '$\\mathtt{p}$', + u'\U0001d69a': '$\\mathtt{q}$', + u'\U0001d69b': '$\\mathtt{r}$', + u'\U0001d69c': '$\\mathtt{s}$', + u'\U0001d69d': '$\\mathtt{t}$', + u'\U0001d69e': '$\\mathtt{u}$', + u'\U0001d69f': '$\\mathtt{v}$', + u'\U0001d6a0': '$\\mathtt{w}$', + u'\U0001d6a1': '$\\mathtt{x}$', + u'\U0001d6a2': '$\\mathtt{y}$', + u'\U0001d6a3': '$\\mathtt{z}$', + u'\U0001d6a8': '$\\mathbf{\\Alpha}$', + u'\U0001d6a9': '$\\mathbf{\\Beta}$', + u'\U0001d6aa': '$\\mathbf{\\Gamma}$', + u'\U0001d6ab': '$\\mathbf{\\Delta}$', + u'\U0001d6ac': '$\\mathbf{\\Epsilon}$', + u'\U0001d6ad': '$\\mathbf{\\Zeta}$', + u'\U0001d6ae': '$\\mathbf{\\Eta}$', + u'\U0001d6af': '$\\mathbf{\\Theta}$', + u'\U0001d6b0': '$\\mathbf{\\Iota}$', + u'\U0001d6b1': '$\\mathbf{\\Kappa}$', + u'\U0001d6b2': '$\\mathbf{\\Lambda}$', + u'\U0001d6b3': '$M$', + u'\U0001d6b4': '$N$', + u'\U0001d6b5': '$\\mathbf{\\Xi}$', + u'\U0001d6b6': '$O$', + u'\U0001d6b7': '$\\mathbf{\\Pi}$', + u'\U0001d6b8': '$\\mathbf{\\Rho}$', + u'\U0001d6b9': '{\\mathbf{\\vartheta}}', + u'\U0001d6ba': '$\\mathbf{\\Sigma}$', + u'\U0001d6bb': '$\\mathbf{\\Tau}$', + u'\U0001d6bc': '$\\mathbf{\\Upsilon}$', + u'\U0001d6bd': '$\\mathbf{\\Phi}$', + u'\U0001d6be': '$\\mathbf{\\Chi}$', + u'\U0001d6bf': '$\\mathbf{\\Psi}$', + u'\U0001d6c0': '$\\mathbf{\\Omega}$', + u'\U0001d6c1': '$\\mathbf{\\nabla}$', + u'\U0001d6c2': '$\\mathbf{\\Alpha}$', + u'\U0001d6c3': '$\\mathbf{\\Beta}$', + u'\U0001d6c4': '$\\mathbf{\\Gamma}$', + u'\U0001d6c5': '$\\mathbf{\\Delta}$', + u'\U0001d6c6': '$\\mathbf{\\Epsilon}$', + u'\U0001d6c7': '$\\mathbf{\\Zeta}$', + u'\U0001d6c8': '$\\mathbf{\\Eta}$', + u'\U0001d6c9': '$\\mathbf{\\theta}$', + u'\U0001d6ca': '$\\mathbf{\\Iota}$', + u'\U0001d6cb': '$\\mathbf{\\Kappa}$', + u'\U0001d6cc': '$\\mathbf{\\Lambda}$', + u'\U0001d6cd': '$M$', + u'\U0001d6ce': '$N$', + u'\U0001d6cf': '$\\mathbf{\\Xi}$', + u'\U0001d6d0': '$O$', + u'\U0001d6d1': '$\\mathbf{\\Pi}$', + u'\U0001d6d2': '$\\mathbf{\\Rho}$', + u'\U0001d6d3': '$\\mathbf{\\varsigma}$', + u'\U0001d6d4': '$\\mathbf{\\Sigma}$', + u'\U0001d6d5': '$\\mathbf{\\Tau}$', + u'\U0001d6d6': '$\\mathbf{\\Upsilon}$', + u'\U0001d6d7': '$\\mathbf{\\Phi}$', + u'\U0001d6d8': '$\\mathbf{\\Chi}$', + u'\U0001d6d9': '$\\mathbf{\\Psi}$', + u'\U0001d6da': '$\\mathbf{\\Omega}$', + u'\U0001d6db': '$\\partial$', + u'\U0001d6dc': '$\\in$', + u'\U0001d6dd': '{\\mathbf{\\vartheta}}', + u'\U0001d6de': '{\\mathbf{\\varkappa}}', + u'\U0001d6df': '{\\mathbf{\\phi}}', + u'\U0001d6e0': '{\\mathbf{\\varrho}}', + u'\U0001d6e1': '{\\mathbf{\\varpi}}', + u'\U0001d6e2': '$\\mathsl{\\Alpha}$', + u'\U0001d6e3': '$\\mathsl{\\Beta}$', + u'\U0001d6e4': '$\\mathsl{\\Gamma}$', + u'\U0001d6e5': '$\\mathsl{\\Delta}$', + u'\U0001d6e6': '$\\mathsl{\\Epsilon}$', + u'\U0001d6e7': '$\\mathsl{\\Zeta}$', + u'\U0001d6e8': '$\\mathsl{\\Eta}$', + u'\U0001d6e9': '$\\mathsl{\\Theta}$', + u'\U0001d6ea': '$\\mathsl{\\Iota}$', + u'\U0001d6eb': '$\\mathsl{\\Kappa}$', + u'\U0001d6ec': '$\\mathsl{\\Lambda}$', + u'\U0001d6ed': '$M$', + u'\U0001d6ee': '$N$', + u'\U0001d6ef': '$\\mathsl{\\Xi}$', + u'\U0001d6f0': '$O$', + u'\U0001d6f1': '$\\mathsl{\\Pi}$', + u'\U0001d6f2': '$\\mathsl{\\Rho}$', + u'\U0001d6f3': '{\\mathsl{\\vartheta}}', + u'\U0001d6f4': '$\\mathsl{\\Sigma}$', + u'\U0001d6f5': '$\\mathsl{\\Tau}$', + u'\U0001d6f6': '$\\mathsl{\\Upsilon}$', + u'\U0001d6f7': '$\\mathsl{\\Phi}$', + u'\U0001d6f8': '$\\mathsl{\\Chi}$', + u'\U0001d6f9': '$\\mathsl{\\Psi}$', + u'\U0001d6fa': '$\\mathsl{\\Omega}$', + u'\U0001d6fb': '$\\mathsl{\\nabla}$', + u'\U0001d6fc': '$\\mathsl{\\Alpha}$', + u'\U0001d6fd': '$\\mathsl{\\Beta}$', + u'\U0001d6fe': '$\\mathsl{\\Gamma}$', + u'\U0001d6ff': '$\\mathsl{\\Delta}$', + u'\U0001d700': '$\\mathsl{\\Epsilon}$', + u'\U0001d701': '$\\mathsl{\\Zeta}$', + u'\U0001d702': '$\\mathsl{\\Eta}$', + u'\U0001d703': '$\\mathsl{\\Theta}$', + u'\U0001d704': '$\\mathsl{\\Iota}$', + u'\U0001d705': '$\\mathsl{\\Kappa}$', + u'\U0001d706': '$\\mathsl{\\Lambda}$', + u'\U0001d707': '$M$', + u'\U0001d708': '$N$', + u'\U0001d709': '$\\mathsl{\\Xi}$', + u'\U0001d70a': '$O$', + u'\U0001d70b': '$\\mathsl{\\Pi}$', + u'\U0001d70c': '$\\mathsl{\\Rho}$', + u'\U0001d70d': '$\\mathsl{\\varsigma}$', + u'\U0001d70e': '$\\mathsl{\\Sigma}$', + u'\U0001d70f': '$\\mathsl{\\Tau}$', + u'\U0001d710': '$\\mathsl{\\Upsilon}$', + u'\U0001d711': '$\\mathsl{\\Phi}$', + u'\U0001d712': '$\\mathsl{\\Chi}$', + u'\U0001d713': '$\\mathsl{\\Psi}$', + u'\U0001d714': '$\\mathsl{\\Omega}$', + u'\U0001d715': '$\\partial$', + u'\U0001d716': '$\\in$', + u'\U0001d717': '{\\mathsl{\\vartheta}}', + u'\U0001d718': '{\\mathsl{\\varkappa}}', + u'\U0001d719': '{\\mathsl{\\phi}}', + u'\U0001d71a': '{\\mathsl{\\varrho}}', + u'\U0001d71b': '{\\mathsl{\\varpi}}', + u'\U0001d71c': '$\\mathbit{\\Alpha}$', + u'\U0001d71d': '$\\mathbit{\\Beta}$', + u'\U0001d71e': '$\\mathbit{\\Gamma}$', + u'\U0001d71f': '$\\mathbit{\\Delta}$', + u'\U0001d720': '$\\mathbit{\\Epsilon}$', + u'\U0001d721': '$\\mathbit{\\Zeta}$', + u'\U0001d722': '$\\mathbit{\\Eta}$', + u'\U0001d723': '$\\mathbit{\\Theta}$', + u'\U0001d724': '$\\mathbit{\\Iota}$', + u'\U0001d725': '$\\mathbit{\\Kappa}$', + u'\U0001d726': '$\\mathbit{\\Lambda}$', + u'\U0001d727': '$M$', + u'\U0001d728': '$N$', + u'\U0001d729': '$\\mathbit{\\Xi}$', + u'\U0001d72a': '$O$', + u'\U0001d72b': '$\\mathbit{\\Pi}$', + u'\U0001d72c': '$\\mathbit{\\Rho}$', + u'\U0001d72d': '{\\mathbit{O}}', + u'\U0001d72e': '$\\mathbit{\\Sigma}$', + u'\U0001d72f': '$\\mathbit{\\Tau}$', + u'\U0001d730': '$\\mathbit{\\Upsilon}$', + u'\U0001d731': '$\\mathbit{\\Phi}$', + u'\U0001d732': '$\\mathbit{\\Chi}$', + u'\U0001d733': '$\\mathbit{\\Psi}$', + u'\U0001d734': '$\\mathbit{\\Omega}$', + u'\U0001d735': '$\\mathbit{\\nabla}$', + u'\U0001d736': '$\\mathbit{\\Alpha}$', + u'\U0001d737': '$\\mathbit{\\Beta}$', + u'\U0001d738': '$\\mathbit{\\Gamma}$', + u'\U0001d739': '$\\mathbit{\\Delta}$', + u'\U0001d73a': '$\\mathbit{\\Epsilon}$', + u'\U0001d73b': '$\\mathbit{\\Zeta}$', + u'\U0001d73c': '$\\mathbit{\\Eta}$', + u'\U0001d73d': '$\\mathbit{\\Theta}$', + u'\U0001d73e': '$\\mathbit{\\Iota}$', + u'\U0001d73f': '$\\mathbit{\\Kappa}$', + u'\U0001d740': '$\\mathbit{\\Lambda}$', + u'\U0001d741': '$M$', + u'\U0001d742': '$N$', + u'\U0001d743': '$\\mathbit{\\Xi}$', + u'\U0001d744': '$O$', + u'\U0001d745': '$\\mathbit{\\Pi}$', + u'\U0001d746': '$\\mathbit{\\Rho}$', + u'\U0001d747': '$\\mathbit{\\varsigma}$', + u'\U0001d748': '$\\mathbit{\\Sigma}$', + u'\U0001d749': '$\\mathbit{\\Tau}$', + u'\U0001d74a': '$\\mathbit{\\Upsilon}$', + u'\U0001d74b': '$\\mathbit{\\Phi}$', + u'\U0001d74c': '$\\mathbit{\\Chi}$', + u'\U0001d74d': '$\\mathbit{\\Psi}$', + u'\U0001d74e': '$\\mathbit{\\Omega}$', + u'\U0001d74f': '$\\partial$', + u'\U0001d750': '$\\in$', + u'\U0001d751': '{\\mathbit{\\vartheta}}', + u'\U0001d752': '{\\mathbit{\\varkappa}}', + u'\U0001d753': '{\\mathbit{\\phi}}', + u'\U0001d754': '{\\mathbit{\\varrho}}', + u'\U0001d755': '{\\mathbit{\\varpi}}', + u'\U0001d756': '$\\mathsfbf{\\Alpha}$', + u'\U0001d757': '$\\mathsfbf{\\Beta}$', + u'\U0001d758': '$\\mathsfbf{\\Gamma}$', + u'\U0001d759': '$\\mathsfbf{\\Delta}$', + u'\U0001d75a': '$\\mathsfbf{\\Epsilon}$', + u'\U0001d75b': '$\\mathsfbf{\\Zeta}$', + u'\U0001d75c': '$\\mathsfbf{\\Eta}$', + u'\U0001d75d': '$\\mathsfbf{\\Theta}$', + u'\U0001d75e': '$\\mathsfbf{\\Iota}$', + u'\U0001d75f': '$\\mathsfbf{\\Kappa}$', + u'\U0001d760': '$\\mathsfbf{\\Lambda}$', + u'\U0001d761': '$M$', + u'\U0001d762': '$N$', + u'\U0001d763': '$\\mathsfbf{\\Xi}$', + u'\U0001d764': '$O$', + u'\U0001d765': '$\\mathsfbf{\\Pi}$', + u'\U0001d766': '$\\mathsfbf{\\Rho}$', + u'\U0001d767': '{\\mathsfbf{\\vartheta}}', + u'\U0001d768': '$\\mathsfbf{\\Sigma}$', + u'\U0001d769': '$\\mathsfbf{\\Tau}$', + u'\U0001d76a': '$\\mathsfbf{\\Upsilon}$', + u'\U0001d76b': '$\\mathsfbf{\\Phi}$', + u'\U0001d76c': '$\\mathsfbf{\\Chi}$', + u'\U0001d76d': '$\\mathsfbf{\\Psi}$', + u'\U0001d76e': '$\\mathsfbf{\\Omega}$', + u'\U0001d76f': '$\\mathsfbf{\\nabla}$', + u'\U0001d770': '$\\mathsfbf{\\Alpha}$', + u'\U0001d771': '$\\mathsfbf{\\Beta}$', + u'\U0001d772': '$\\mathsfbf{\\Gamma}$', + u'\U0001d773': '$\\mathsfbf{\\Delta}$', + u'\U0001d774': '$\\mathsfbf{\\Epsilon}$', + u'\U0001d775': '$\\mathsfbf{\\Zeta}$', + u'\U0001d776': '$\\mathsfbf{\\Eta}$', + u'\U0001d777': '$\\mathsfbf{\\Theta}$', + u'\U0001d778': '$\\mathsfbf{\\Iota}$', + u'\U0001d779': '$\\mathsfbf{\\Kappa}$', + u'\U0001d77a': '$\\mathsfbf{\\Lambda}$', + u'\U0001d77b': '$M$', + u'\U0001d77c': '$N$', + u'\U0001d77d': '$\\mathsfbf{\\Xi}$', + u'\U0001d77e': '$O$', + u'\U0001d77f': '$\\mathsfbf{\\Pi}$', + u'\U0001d780': '$\\mathsfbf{\\Rho}$', + u'\U0001d781': '$\\mathsfbf{\\varsigma}$', + u'\U0001d782': '$\\mathsfbf{\\Sigma}$', + u'\U0001d783': '$\\mathsfbf{\\Tau}$', + u'\U0001d784': '$\\mathsfbf{\\Upsilon}$', + u'\U0001d785': '$\\mathsfbf{\\Phi}$', + u'\U0001d786': '$\\mathsfbf{\\Chi}$', + u'\U0001d787': '$\\mathsfbf{\\Psi}$', + u'\U0001d788': '$\\mathsfbf{\\Omega}$', + u'\U0001d789': '$\\partial$', + u'\U0001d78a': '$\\in$', + u'\U0001d78b': '{\\mathsfbf{\\vartheta}}', + u'\U0001d78c': '{\\mathsfbf{\\varkappa}}', + u'\U0001d78d': '{\\mathsfbf{\\phi}}', + u'\U0001d78e': '{\\mathsfbf{\\varrho}}', + u'\U0001d78f': '{\\mathsfbf{\\varpi}}', + u'\U0001d790': '$\\mathsfbfsl{\\Alpha}$', + u'\U0001d791': '$\\mathsfbfsl{\\Beta}$', + u'\U0001d792': '$\\mathsfbfsl{\\Gamma}$', + u'\U0001d793': '$\\mathsfbfsl{\\Delta}$', + u'\U0001d794': '$\\mathsfbfsl{\\Epsilon}$', + u'\U0001d795': '$\\mathsfbfsl{\\Zeta}$', + u'\U0001d796': '$\\mathsfbfsl{\\Eta}$', + u'\U0001d797': '$\\mathsfbfsl{\\vartheta}$', + u'\U0001d798': '$\\mathsfbfsl{\\Iota}$', + u'\U0001d799': '$\\mathsfbfsl{\\Kappa}$', + u'\U0001d79a': '$\\mathsfbfsl{\\Lambda}$', + u'\U0001d79b': '$M$', + u'\U0001d79c': '$N$', + u'\U0001d79d': '$\\mathsfbfsl{\\Xi}$', + u'\U0001d79e': '$O$', + u'\U0001d79f': '$\\mathsfbfsl{\\Pi}$', + u'\U0001d7a0': '$\\mathsfbfsl{\\Rho}$', + u'\U0001d7a1': '{\\mathsfbfsl{\\vartheta}}', + u'\U0001d7a2': '$\\mathsfbfsl{\\Sigma}$', + u'\U0001d7a3': '$\\mathsfbfsl{\\Tau}$', + u'\U0001d7a4': '$\\mathsfbfsl{\\Upsilon}$', + u'\U0001d7a5': '$\\mathsfbfsl{\\Phi}$', + u'\U0001d7a6': '$\\mathsfbfsl{\\Chi}$', + u'\U0001d7a7': '$\\mathsfbfsl{\\Psi}$', + u'\U0001d7a8': '$\\mathsfbfsl{\\Omega}$', + u'\U0001d7a9': '$\\mathsfbfsl{\\nabla}$', + u'\U0001d7aa': '$\\mathsfbfsl{\\Alpha}$', + u'\U0001d7ab': '$\\mathsfbfsl{\\Beta}$', + u'\U0001d7ac': '$\\mathsfbfsl{\\Gamma}$', + u'\U0001d7ad': '$\\mathsfbfsl{\\Delta}$', + u'\U0001d7ae': '$\\mathsfbfsl{\\Epsilon}$', + u'\U0001d7af': '$\\mathsfbfsl{\\Zeta}$', + u'\U0001d7b0': '$\\mathsfbfsl{\\Eta}$', + u'\U0001d7b1': '$\\mathsfbfsl{\\vartheta}$', + u'\U0001d7b2': '$\\mathsfbfsl{\\Iota}$', + u'\U0001d7b3': '$\\mathsfbfsl{\\Kappa}$', + u'\U0001d7b4': '$\\mathsfbfsl{\\Lambda}$', + u'\U0001d7b5': '$M$', + u'\U0001d7b6': '$N$', + u'\U0001d7b7': '$\\mathsfbfsl{\\Xi}$', + u'\U0001d7b8': '$O$', + u'\U0001d7b9': '$\\mathsfbfsl{\\Pi}$', + u'\U0001d7ba': '$\\mathsfbfsl{\\Rho}$', + u'\U0001d7bb': '$\\mathsfbfsl{\\varsigma}$', + u'\U0001d7bc': '$\\mathsfbfsl{\\Sigma}$', + u'\U0001d7bd': '$\\mathsfbfsl{\\Tau}$', + u'\U0001d7be': '$\\mathsfbfsl{\\Upsilon}$', + u'\U0001d7bf': '$\\mathsfbfsl{\\Phi}$', + u'\U0001d7c0': '$\\mathsfbfsl{\\Chi}$', + u'\U0001d7c1': '$\\mathsfbfsl{\\Psi}$', + u'\U0001d7c2': '$\\mathsfbfsl{\\Omega}$', + u'\U0001d7c3': '$\\partial$', + u'\U0001d7c4': '$\\in$', + u'\U0001d7c5': '{\\mathsfbfsl{\\vartheta}}', + u'\U0001d7c6': '{\\mathsfbfsl{\\varkappa}}', + u'\U0001d7c7': '{\\mathsfbfsl{\\phi}}', + u'\U0001d7c8': '{\\mathsfbfsl{\\varrho}}', + u'\U0001d7c9': '{\\mathsfbfsl{\\varpi}}', + u'\U0001d7ce': '$\\mathbf{0}$', + u'\U0001d7cf': '$\\mathbf{1}$', + u'\U0001d7d0': '$\\mathbf{2}$', + u'\U0001d7d1': '$\\mathbf{3}$', + u'\U0001d7d2': '$\\mathbf{4}$', + u'\U0001d7d3': '$\\mathbf{5}$', + u'\U0001d7d4': '$\\mathbf{6}$', + u'\U0001d7d5': '$\\mathbf{7}$', + u'\U0001d7d6': '$\\mathbf{8}$', + u'\U0001d7d7': '$\\mathbf{9}$', + u'\U0001d7d8': '$\\mathbb{0}$', + u'\U0001d7d9': '$\\mathbb{1}$', + u'\U0001d7da': '$\\mathbb{2}$', + u'\U0001d7db': '$\\mathbb{3}$', + u'\U0001d7dc': '$\\mathbb{4}$', + u'\U0001d7dd': '$\\mathbb{5}$', + u'\U0001d7de': '$\\mathbb{6}$', + u'\U0001d7df': '$\\mathbb{7}$', + u'\U0001d7e0': '$\\mathbb{8}$', + u'\U0001d7e1': '$\\mathbb{9}$', + u'\U0001d7e2': '$\\mathsf{0}$', + u'\U0001d7e3': '$\\mathsf{1}$', + u'\U0001d7e4': '$\\mathsf{2}$', + u'\U0001d7e5': '$\\mathsf{3}$', + u'\U0001d7e6': '$\\mathsf{4}$', + u'\U0001d7e7': '$\\mathsf{5}$', + u'\U0001d7e8': '$\\mathsf{6}$', + u'\U0001d7e9': '$\\mathsf{7}$', + u'\U0001d7ea': '$\\mathsf{8}$', + u'\U0001d7eb': '$\\mathsf{9}$', + u'\U0001d7ec': '$\\mathsfbf{0}$', + u'\U0001d7ed': '$\\mathsfbf{1}$', + u'\U0001d7ee': '$\\mathsfbf{2}$', + u'\U0001d7ef': '$\\mathsfbf{3}$', + u'\U0001d7f0': '$\\mathsfbf{4}$', + u'\U0001d7f1': '$\\mathsfbf{5}$', + u'\U0001d7f2': '$\\mathsfbf{6}$', + u'\U0001d7f3': '$\\mathsfbf{7}$', + u'\U0001d7f4': '$\\mathsfbf{8}$', + u'\U0001d7f5': '$\\mathsfbf{9}$', + u'\U0001d7f6': '$\\mathtt{0}$', + u'\U0001d7f7': '$\\mathtt{1}$', + u'\U0001d7f8': '$\\mathtt{2}$', + u'\U0001d7f9': '$\\mathtt{3}$', + u'\U0001d7fa': '$\\mathtt{4}$', + u'\U0001d7fb': '$\\mathtt{5}$', + u'\U0001d7fc': '$\\mathtt{6}$', + u'\U0001d7fd': '$\\mathtt{7}$', + u'\U0001d7fe': '$\\mathtt{8}$', + u'\U0001d7ff': '$\\mathtt{9}$' + } + +entity_mapping = { + '—':'{---}', + '–':'{--}', + '"':'{"}', + } + +def ValidateCitationKey(text): + """ + removes characters not allowed in BibTeX keys + + >>> from bibliograph.core.utils import _validKey + >>> _validKey(DummyEntry('Foo Bar')) + 'FooBar' + + >>> _validKey(DummyEntry('my@id')) + 'myid' + + """ + # This substitution is based on the description of cite key restrictions at + # http://bibdesk.sourceforge.net/manual/BibDesk%20Help_2.html + return re.sub(u'[ "@\',\\#}{~%&$^]', u'', text) + +def BraceUppercase(text): + """ Convert uppercase letters to bibtex encoded uppercase + + >>> from bibliograph.core.utils import _braceUppercase + >>> _braceUppercase('foo bar') + 'foo bar' + + >>> _braceUppercase('Foo Bar') + '{F}oo {B}ar' + """ + for uc in string.uppercase: + text = text.replace(uc, u'{%s}' % uc) + return text + +def resolveEntities(text): + for entity, entity_map in entity_mapping.iteritems(): + text = text.replace(entity, entity_map) + return text + +def resolveUnicode(text): + #UTF-8 text as entry + for unichar, latexenc in utf8enc2latex_mapping.iteritems() : + text = text.replace(unichar, latexenc) + return text.replace(u'$}{$', u'') + +def escapeSpecialCharacters(text): + """ + latex escaping some (not all) special characters + """ + text.replace('\\', '\\\\') + escape = ['~', '#', '&', '%', '_'] + for c in escape: + text = text.replace(c, '\\' + c ) + return text + +#Calibre functions +#Go from an unicode entry to ASCII Bibtex format without encoding +#Option to go to official ASCII Bibtex or unofficial UTF-8 +def utf8ToBibtex(text, asccii_bibtex = True): + if len(text) == 0: + return '' + text.replace('\\', '\\\\') + text = resolveEntities(text) + if asccii_bibtex : + text = resolveUnicode(text) + return escapeSpecialCharacters(text) + +def bibtex_author_format(item): + #Format authors for Bibtex compliance (get a list as input) + return utf8ToBibtex(u' and'.join([author for author in item])) From 05bb257d8cac771a134d461312f626845e1b3734 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Fri, 16 Jul 2010 17:37:33 +0100 Subject: [PATCH 37/38] Add VALID_DATA_TYPES to FieldMetadata, and also add datatype checks where needed. --- src/calibre/library/custom_columns.py | 5 +++++ src/calibre/library/field_metadata.py | 12 ++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/calibre/library/custom_columns.py b/src/calibre/library/custom_columns.py index e039f5a817..5b459c6d2a 100644 --- a/src/calibre/library/custom_columns.py +++ b/src/calibre/library/custom_columns.py @@ -12,6 +12,7 @@ from math import floor from calibre import prints from calibre.constants import preferred_encoding +from calibre.library.field_metadata import FieldMetadata from calibre.utils.date import parse_date class CustomColumns(object): @@ -30,6 +31,10 @@ class CustomColumns(object): def __init__(self): + # Verify that CUSTOM_DATA_TYPES is a (possibly improper) subset of + # VALID_DATA_TYPES + if len(self.CUSTOM_DATA_TYPES - FieldMetadata.VALID_DATA_TYPES) > 0: + raise ValueError('Unknown custom column type in set') # Delete marked custom columns for record in self.conn.get( 'SELECT id FROM custom_columns WHERE mark_for_delete=1'): diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index 626683fee5..f29b432eec 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -30,8 +30,8 @@ class FieldMetadata(dict): label: the actual column label. No prefixing. - datatype: the type of the information in the field. Valid values are float, - int, rating, bool, comments, datetime, text. + datatype: the type of information in the field. Valid values are listed in + VALID_DATA_TYPES below. is_multiple: valid for the text datatype. If None, the field is to be treated as a single term. If not None, it contains a string, and the field is assumed to contain a list of terms separated by that string @@ -65,6 +65,10 @@ class FieldMetadata(dict): rec_index: the index of the field in the db metadata record. ''' + + VALID_DATA_TYPES = frozenset([None, 'rating', 'text', 'comments', 'datetime', + 'int', 'float', 'bool', 'series']) + _field_metadata = [ ('authors', {'table':'authors', 'column':'name', @@ -296,6 +300,8 @@ class FieldMetadata(dict): self._search_term_map = {} self.custom_label_to_key_map = {} for k,v in self._field_metadata: + if v['kind'] == 'field' and v['datatype'] not in self.VALID_DATA_TYPES: + raise ValueError('Unknown datatype %s for field %s'%(v['datatype'], k)) self._tb_cats[k] = v self._tb_cats[k]['label'] = k self._tb_cats[k]['display'] = {} @@ -377,6 +383,8 @@ class FieldMetadata(dict): key = self.custom_field_prefix + label if key in self._tb_cats: raise ValueError('Duplicate custom field [%s]'%(label)) + if datatype not in self.VALID_DATA_TYPES: + raise ValueError('Unknown datatype %s for field %s'%(datatype, key)) self._tb_cats[key] = {'table':table, 'column':column, 'datatype':datatype, 'is_multiple':is_multiple, 'kind':'field', 'name':name, From e8a2d7b647cb814c98955ea0e6b830f3da78af28 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 16 Jul 2010 11:56:48 -0600 Subject: [PATCH 38/38] Fix long series or publisher names causing convert dialog to become too wide --- src/calibre/gui2/convert/metadata.ui | 51 ++++++++++----------- src/calibre/gui2/dialogs/metadata_single.ui | 9 ---- src/calibre/gui2/widgets.py | 1 + 3 files changed, 25 insertions(+), 36 deletions(-) diff --git a/src/calibre/gui2/convert/metadata.ui b/src/calibre/gui2/convert/metadata.ui index ec5a913f18..7bc45e234e 100644 --- a/src/calibre/gui2/convert/metadata.ui +++ b/src/calibre/gui2/convert/metadata.ui @@ -20,6 +20,30 @@ Book Cover + + + + + + + 0 + 0 + + + + + + + + + + Use cover from &source file + + + true + + + @@ -71,30 +95,6 @@ - - - - Use cover from &source file - - - true - - - - - - - - - - 0 - 0 - - - - - - opt_prefer_metadata_cover @@ -232,9 +232,6 @@ QComboBox::InsertAlphabetically - - QComboBox::AdjustToContents - diff --git a/src/calibre/gui2/dialogs/metadata_single.ui b/src/calibre/gui2/dialogs/metadata_single.ui index 4efb48d870..5da9d37d04 100644 --- a/src/calibre/gui2/dialogs/metadata_single.ui +++ b/src/calibre/gui2/dialogs/metadata_single.ui @@ -277,12 +277,6 @@ - - - 0 - 0 - - List of known series. You can add new series. @@ -295,9 +289,6 @@ QComboBox::InsertAlphabetically - - QComboBox::AdjustToContents - diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 97758482fc..994fa4575f 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -490,6 +490,7 @@ class EnComboBox(QComboBox): QComboBox.__init__(self, *args) self.setLineEdit(EnLineEdit(self)) self.setAutoCompletionCaseSensitivity(Qt.CaseSensitive) + self.setMinimumContentsLength(20) def text(self): return unicode(self.currentText())